[romprefix] Do not check for BBS compatibility
[people/pcmattman/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 FILE_LICENCE ( GPL2_OR_LATER )
10
11 #include <config/general.h>
12
13 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
14 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
15 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
16 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
17 #define PMM_ALLOCATE 0x0000
18 #define PMM_FIND 0x0001
19 #define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \
20                           ( ( 'E' - 'A' + 1 ) << 21 ) + \
21                           ( ( 'N' - 'A' + 1 ) << 16 ) )
22 #define PMM_HANDLE_BASE_IMAGE_SOURCE \
23         ( PMM_HANDLE_BASE | 0x00001000 )
24 #define PMM_HANDLE_BASE_DECOMPRESS_TO \
25         ( PMM_HANDLE_BASE | 0x00002000 )
26
27 /* ROM banner timeout.  Based on the configurable BANNER_TIMEOUT in
28  * config.h, but converted to a number of (18Hz) timer ticks, and
29  * doubled to allow for BIOSes that switch video modes immediately
30  * beforehand, so rendering the message almost invisible to the user.
31  */
32 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
33
34 /* Allow payload to be excluded from ROM size
35  */
36 #if ROMPREFIX_EXCLUDE_PAYLOAD
37 #define ZINFO_TYPE_ADxB "ADHB"
38 #define ZINFO_TYPE_ADxW "ADHW"
39 #else
40 #define ZINFO_TYPE_ADxB "ADDB"
41 #define ZINFO_TYPE_ADxW "ADDW"
42 #endif
43
44         .text
45         .code16
46         .arch i386
47         .section ".prefix", "ax", @progbits
48         
49         .org    0x00
50 romheader:
51         .word   0xAA55                  /* BIOS extension signature */
52 romheader_size: .byte 0                 /* Size in 512-byte blocks */
53         jmp     init                    /* Initialisation vector */
54 checksum:
55         .byte   0
56         .org    0x16
57         .word   undiheader
58         .org    0x18
59         .word   pciheader
60         .org    0x1a
61         .word   pnpheader
62         .size romheader, . - romheader
63
64         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
65         .ascii  ZINFO_TYPE_ADxB
66         .long   romheader_size
67         .long   512
68         .long   0
69         .previous
70
71 build_id:
72         .long   _build_id               /* Randomly-generated build ID */
73
74 pciheader:
75         .ascii  "PCIR"                  /* Signature */
76         .word   pci_vendor_id           /* Vendor identification */ 
77         .word   pci_device_id           /* Device identification */
78         .word   0x0000                  /* Device list pointer */
79         .word   pciheader_len           /* PCI data structure length */
80         .byte   0x03                    /* PCI data structure revision */
81         .byte   0x02, 0x00, 0x00        /* Class code */
82 pciheader_image_length:
83         .word   0                       /* Image length */
84         .word   0x0001                  /* Revision level */
85         .byte   0x00                    /* Code type */
86         .byte   0x80                    /* Last image indicator */
87 pciheader_runtime_length:
88         .word   0                       /* Maximum run-time image length */
89         .word   0x0000                  /* Configuration utility code header */
90         .word   0x0000                  /* DMTF CLP entry point */
91         .equ pciheader_len, . - pciheader
92         .size pciheader, . - pciheader
93
94         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
95         .ascii  ZINFO_TYPE_ADxW
96         .long   pciheader_image_length
97         .long   512
98         .long   0
99         .ascii  ZINFO_TYPE_ADxW
100         .long   pciheader_runtime_length
101         .long   512
102         .long   0
103         .previous
104
105 pnpheader:
106         .ascii  "$PnP"                  /* Signature */
107         .byte   0x01                    /* Structure revision */
108         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
109         .word   0x0000                  /* Offset of next header */
110         .byte   0x00                    /* Reserved */
111         .byte   0x00                    /* Checksum */
112         .long   0x00000000              /* Device identifier */
113         .word   mfgstr                  /* Manufacturer string */
114         .word   prodstr                 /* Product name */
115         .byte   0x02                    /* Device base type code */
116         .byte   0x00                    /* Device sub-type code */
117         .byte   0x00                    /* Device interface type code */
118         .byte   0xf4                    /* Device indicator */
119         .word   0x0000                  /* Boot connection vector */
120         .word   0x0000                  /* Disconnect vector */
121         .word   bev_entry               /* Boot execution vector */
122         .word   0x0000                  /* Reserved */
123         .word   0x0000                  /* Static resource information vector*/
124         .equ pnpheader_len, . - pnpheader
125         .size pnpheader, . - pnpheader
126
127 /* Manufacturer string */
128 mfgstr:
129         .asciz  "http://etherboot.org"
130         .size mfgstr, . - mfgstr
131
132 /* Product string
133  *
134  * Defaults to PRODUCT_SHORT_NAME.  If the ROM image is writable at
135  * initialisation time, it will be filled in to include the PCI
136  * bus:dev.fn number of the card as well.
137  */
138 prodstr:
139         .ascii  PRODUCT_SHORT_NAME
140 prodstr_separator:
141         .byte   0
142         .ascii  "(PCI "
143 prodstr_pci_id:
144         .asciz  "xx:xx.x)"              /* Filled in by init code */
145         .size prodstr, . - prodstr
146
147         .globl  undiheader      
148         .weak   undiloader
149 undiheader:
150         .ascii  "UNDI"                  /* Signature */
151         .byte   undiheader_len          /* Length of structure */
152         .byte   0                       /* Checksum */
153         .byte   0                       /* Structure revision */
154         .byte   0,1,2                   /* PXE version: 2.1.0 */
155         .word   undiloader              /* Offset to loader routine */
156         .word   _data16_memsz           /* Stack segment size */
157         .word   _data16_memsz           /* Data segment size */
158         .word   _text16_memsz           /* Code segment size */
159         .ascii  "PCIR"                  /* Bus type */
160         .equ undiheader_len, . - undiheader
161         .size undiheader, . - undiheader
162
163 /* Initialisation (called once during POST)
164  *
165  * Determine whether or not this is a PnP system via a signature
166  * check.  If it is PnP, return to the PnP BIOS indicating that we are
167  * a boot-capable device; the BIOS will call our boot execution vector
168  * if it wants to boot us.  If it is not PnP, hook INT 19.
169  */
170 init:
171         /* Preserve registers, clear direction flag, set %ds=%cs */
172         pushaw
173         pushw   %ds
174         pushw   %es
175         pushw   %fs
176         pushw   %gs
177         cld
178         pushw   %cs
179         popw    %ds
180
181         /* Shuffle some registers around.  We need %di available for
182          * the print_xxx functions, and in a register that's
183          * addressable from %es, so shuffle as follows:
184          *
185          *    %di (pointer to PnP structure) => %bx
186          *    %bx (runtime segment address, for PCI 3.0) => %gs
187          */
188         movw    %bx, %gs
189         movw    %di, %bx
190
191         /* Store PCI bus:dev.fn address */
192         movw    %ax, init_pci_busdevfn
193
194         /* Print message as early as possible */
195         movw    $init_message, %si
196         xorw    %di, %di
197         call    print_message
198         call    print_pci_busdevfn
199
200         /* Fill in product name string, if possible */
201         movw    $prodstr_pci_id, %di
202         call    print_pci_busdevfn
203         movb    $( ' ' ), prodstr_separator
204
205         /* Print segment address */
206         movb    $( ' ' ), %al
207         xorw    %di, %di
208         call    print_character
209         movw    %cs, %ax
210         call    print_hex_word
211
212         /* Check for PCI BIOS version */
213         pushl   %ebx
214         pushl   %edx
215         pushl   %edi
216         stc
217         movw    $0xb101, %ax
218         int     $0x1a
219         jc      no_pci3
220         cmpl    $PCI_SIGNATURE, %edx
221         jne     no_pci3
222         testb   %ah, %ah
223         jnz     no_pci3
224         movw    $init_message_pci, %si
225         xorw    %di, %di
226         call    print_message
227         movb    %bh, %al
228         call    print_hex_nibble
229         movb    $( '.' ), %al
230         call    print_character
231         movb    %bl, %al
232         call    print_hex_byte
233         cmpb    $3, %bh
234         jb      no_pci3
235         /* PCI >=3.0: leave %gs as-is if sane */
236         movw    %gs, %ax
237         cmpw    $0xa000, %ax    /* Insane if %gs < 0xa000 */
238         jb      pci3_insane
239         movw    %cs, %bx        /* Sane if %cs == %gs */
240         cmpw    %bx, %ax
241         je      1f
242         movzbw  romheader_size, %cx /* Sane if %cs+len <= %gs */
243         shlw    $5, %cx
244         addw    %cx, %bx
245         cmpw    %bx, %ax
246         jae     1f
247         movw    %cs, %bx        /* Sane if %gs+len <= %cs */
248         addw    %cx, %ax
249         cmpw    %bx, %ax
250         jbe     1f
251 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
252         movb    $( '!' ), %al
253         call    print_character
254         movw    %gs, %ax
255         call    print_hex_word
256 no_pci3:
257         /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
258         pushw   %cs
259         popw    %gs
260 1:      popl    %edi
261         popl    %edx
262         popl    %ebx
263
264         /* Check for PnP BIOS.  Although %es:di should point to the
265          * PnP BIOS signature on entry, some BIOSes fail to do this.
266          */
267         movw    $( 0xf000 - 1 ), %bx
268 pnp_scan:
269         incw    %bx
270         jz      no_pnp
271         movw    %bx, %es
272         cmpl    $PNP_SIGNATURE, %es:0
273         jne     pnp_scan
274         xorw    %dx, %dx
275         xorw    %si, %si
276         movzbw  %es:5, %cx
277 1:      es lodsb
278         addb    %al, %dl
279         loop    1b
280         jnz     pnp_scan
281         /* Is PnP: print PnP message */
282         movw    $init_message_pnp, %si
283         xorw    %di, %di
284         call    print_message
285         jmp     pnp_done
286 no_pnp: /* Not PnP-compliant - hook INT 19 */
287         movw    $init_message_int19, %si
288         xorw    %di, %di
289         call    print_message
290         xorw    %ax, %ax
291         movw    %ax, %es
292         pushl   %es:( 0x19 * 4 )
293         popl    orig_int19
294         pushw   %gs /* %gs contains runtime %cs */
295         pushw   $int19_entry
296         popl    %es:( 0x19 * 4 )
297 pnp_done:
298
299         /* Check for PMM */
300         movw    $( 0xe000 - 1 ), %bx
301 pmm_scan:
302         incw    %bx
303         jz      no_pmm
304         movw    %bx, %es
305         cmpl    $PMM_SIGNATURE, %es:0
306         jne     pmm_scan
307         xorw    %dx, %dx
308         xorw    %si, %si
309         movzbw  %es:5, %cx
310 1:      es lodsb
311         addb    %al, %dl
312         loop    1b
313         jnz     pmm_scan
314         /* PMM found: print PMM message */
315         movw    $init_message_pmm, %si
316         xorw    %di, %di
317         call    print_message
318         /* We have PMM and so a 1kB stack: preserve whole registers */
319         pushal
320         /* Allocate image source PMM block */
321         movzwl  image_source_len_pgh, %ecx
322         movl    $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx
323         movw    $get_pmm_image_source, %bp
324         call    get_pmm
325         movl    %esi, image_source
326         jc      1f
327         /* Copy ROM to image source PMM block */
328         pushw   %es
329         xorw    %ax, %ax
330         movw    %ax, %es
331         movl    %esi, %edi
332         xorl    %esi, %esi
333         movzbl  romheader_size, %ecx
334         shll    $9, %ecx
335         addr32 rep movsb        /* PMM presence implies flat real mode */
336         popw    %es
337         /* Shrink ROM */
338         movb    shrunk_rom_size, %al
339         movb    %al, romheader_size
340 1:      /* Allocate decompression PMM block.  Round up the size to the
341          * nearest 128kB and use the size within the PMM handle; this
342          * allows the same decompression area to be shared between
343          * multiple gPXE ROMs even with differing build IDs
344          */
345         movl    $_textdata_memsz_pgh, %ecx
346         addl    $0x00001fff, %ecx
347         andl    $0xffffe000, %ecx
348         movl    %ecx, %ebx
349         shrw    $12, %bx
350         orl     $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx
351         movw    $get_pmm_decompress_to, %bp
352         call    get_pmm
353         movl    %esi, decompress_to
354         /* Restore registers */
355         popal
356 no_pmm:
357
358         /* Update checksum */
359         xorw    %bx, %bx
360         xorw    %si, %si
361         movzbw  romheader_size, %cx
362         shlw    $9, %cx
363 1:      lodsb
364         addb    %al, %bl
365         loop    1b
366         subb    %bl, checksum
367
368         /* Copy self to option ROM space.  Required for PCI3.0, which
369          * loads us to a temporary location in low memory.  Will be a
370          * no-op for lower PCI versions.
371          */
372         movb    $( ' ' ), %al
373         xorw    %di, %di
374         call    print_character
375         movw    %gs, %ax
376         call    print_hex_word
377         movzbw  romheader_size, %cx
378         shlw    $9, %cx
379         movw    %ax, %es
380         xorw    %si, %si
381         xorw    %di, %di
382         cs rep  movsb
383
384         /* Prompt for POST-time shell */
385         movw    $init_message_prompt, %si
386         xorw    %di, %di
387         call    print_message
388         movw    $prodstr, %si
389         call    print_message
390         movw    $init_message_dots, %si
391         call    print_message
392         /* Wait for Ctrl-B */
393         movw    $0xff02, %bx
394         call    wait_for_key
395         /* Clear prompt */
396         pushf
397         xorw    %di, %di
398         call    print_kill_line
399         movw    $init_message_done, %si
400         call    print_message
401         popf
402         jnz     2f
403         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
404          * picked up by the initial shell prompt, and we will drop
405          * into a shell.
406          */
407         stc                     /* Inhibit relocation */
408         pushw   %cs
409         call    exec
410 2:
411         /* Restore registers */
412         popw    %gs
413         popw    %fs
414         popw    %es
415         popw    %ds
416         popaw
417
418         /* Indicate boot capability to PnP BIOS, if present */
419         movw    $0x20, %ax
420         lret
421         .size init, . - init
422
423 /* Attempt to find or allocate PMM block
424  *
425  * Parameters:
426  *  %ecx : size of block to allocate, in paragraphs
427  *  %ebx : PMM handle base
428  *  %bp : routine to check acceptability of found blocks
429  *  %es:0000 : PMM structure
430  * Returns:
431  *  %ebx : PMM handle
432  *  %esi : allocated block address, or zero (with CF set) if allocation failed
433  */
434 get_pmm:
435         /* Preserve registers */
436         pushl   %eax
437         pushw   %di
438         movw    $' ', %di
439 get_pmm_find:
440         /* Try to find existing block */
441         pushl   %ebx            /* PMM handle */
442         pushw   $PMM_FIND
443         lcall   *%es:7
444         addw    $6, %sp
445         pushw   %dx
446         pushw   %ax
447         popl    %esi
448         testl   %esi, %esi
449         jz      get_pmm_allocate
450         /* Block found - check acceptability */
451         call    *%bp
452         jnc     get_pmm_done
453         /* Block not acceptable - increment handle and retry */
454         incl    %ebx
455         jmp     get_pmm_find
456 get_pmm_allocate:
457         /* Block not found - try to allocate new block */
458         pushw   $0x0002         /* Extended memory */
459         pushl   %ebx            /* PMM handle */
460         pushl   %ecx            /* Length */
461         pushw   $PMM_ALLOCATE
462         lcall   *%es:7
463         addw    $12, %sp
464         pushw   %dx
465         pushw   %ax
466         popl    %esi
467         movw    $'+', %di       /* Indicate allocation attempt */
468         testl   %esi, %esi
469         jnz     get_pmm_done
470         stc
471 get_pmm_done:
472         /* Print block address */
473         pushfw
474         movw    %di, %ax
475         xorw    %di, %di
476         call    print_character
477         movl    %esi, %eax
478         call    print_hex_dword
479         popfw
480         /* Restore registers and return */
481         popw    %di
482         popl    %eax
483         ret
484         .size   get_pmm, . - get_pmm
485
486         /* Check acceptability of image source block */
487 get_pmm_image_source:
488         pushw   %es
489         xorw    %ax, %ax
490         movw    %ax, %es
491         movl    build_id, %eax
492         cmpl    %es:build_id(%esi), %eax
493         je      1f
494         stc
495 1:      popw    %es
496         ret
497         .size   get_pmm_image_source, . - get_pmm_image_source
498
499         /* Check acceptability of decompression block */
500 get_pmm_decompress_to:
501         clc
502         ret
503         .size   get_pmm_decompress_to, . - get_pmm_decompress_to
504
505 /*
506  * Note to hardware vendors:
507  *
508  * If you wish to brand this boot ROM, please do so by defining the
509  * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
510  *
511  * While nothing in the GPL prevents you from removing all references
512  * to gPXE or http://etherboot.org, we prefer you not to do so.
513  *
514  * If you have an OEM-mandated branding requirement that cannot be
515  * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
516  * please contact us.
517  *
518  * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
519  *   bypassing the spirit of this request! ]
520  */
521 init_message:
522         .ascii  "\n"
523         .ascii  PRODUCT_NAME
524         .ascii  "\n"
525         .asciz  "gPXE (http://etherboot.org) - "
526         .size   init_message, . - init_message
527 init_message_pci:
528         .asciz  " PCI"
529         .size   init_message_pci, . - init_message_pci
530 init_message_pnp:
531         .asciz  " PnP"
532         .size   init_message_pnp, . - init_message_pnp
533 init_message_pmm:
534         .asciz  " PMM"
535         .size   init_message_pmm, . - init_message_pmm
536 init_message_int19:
537         .asciz  " INT19"
538         .size   init_message_int19, . - init_message_int19
539 init_message_prompt:
540         .asciz  "\nPress Ctrl-B to configure "
541         .size   init_message_prompt, . - init_message_prompt
542 init_message_dots:
543         .asciz  "..."
544         .size   init_message_dots, . - init_message_dots
545 init_message_done:
546         .asciz  "\n\n"
547         .size   init_message_done, . - init_message_done
548
549 /* PCI bus:dev.fn
550  *
551  */
552 init_pci_busdevfn:
553         .word   0xffff
554         .size   init_pci_busdevfn, . - init_pci_busdevfn
555
556 /* Image source area
557  *
558  * May be either zero (indicating to use option ROM space as source),
559  * or within a PMM-allocated block.
560  */
561         .globl  image_source
562 image_source:
563         .long   0
564         .size   image_source, . - image_source
565
566 /* Image source area length (in paragraphs)
567  *
568  */
569 image_source_len_pgh:
570         .word   0
571         .size   image_source_len_pgh, . - image_source_len_pgh
572         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
573         .ascii  "ADDW"
574         .long   image_source_len_pgh
575         .long   16
576         .long   0
577         .previous
578
579 /* Shrunk ROM size (in 512-byte sectors)
580  *
581  */
582 shrunk_rom_size:
583         .byte   0
584         .size   shrunk_rom_size, . - shrunk_rom_size
585         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
586         .ascii  "ADHB"
587         .long   shrunk_rom_size
588         .long   512
589         .long   0
590         .previous
591
592 /* Temporary decompression area
593  *
594  * May be either zero (indicating to use default decompression area in
595  * high memory), or within a PMM-allocated block.
596  */
597         .globl  decompress_to
598 decompress_to:
599         .long   0
600         .size   decompress_to, . - decompress_to
601
602 /* Boot Execution Vector entry point
603  *
604  * Called by the PnP BIOS when it wants to boot us.
605  */
606 bev_entry:
607         clc                     /* Allow relocation */
608         pushw   %cs
609         call    exec
610         lret
611         .size   bev_entry, . - bev_entry
612
613 /* INT19 entry point
614  *
615  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
616  * attempt to return via the original INT 19 vector (if we were able
617  * to store it).
618  */
619 int19_entry:
620         pushw   %cs
621         popw    %ds
622         /* Prompt user to press B to boot */
623         movw    $int19_message_prompt, %si
624         xorw    %di, %di
625         call    print_message
626         movw    $prodstr, %si
627         call    print_message
628         movw    $int19_message_dots, %si
629         call    print_message
630         movw    $0xdf4e, %bx
631         call    wait_for_key
632         pushf
633         xorw    %di, %di
634         call    print_kill_line
635         movw    $int19_message_done, %si
636         call    print_message
637         popf
638         jz      1f
639         /* Leave keypress in buffer and start gPXE.  The keypress will
640          * cause the usual initial Ctrl-B prompt to be skipped.
641          */
642         clc                     /* Allow relocation */
643         pushw   %cs
644         call    exec
645 1:      /* Try to call original INT 19 vector */
646         movl    %cs:orig_int19, %eax
647         testl   %eax, %eax
648         je      2f
649         ljmp    *%cs:orig_int19
650 2:      /* No chained vector: issue INT 18 as a last resort */
651         int     $0x18
652         .size   int19_entry, . - int19_entry
653 orig_int19:
654         .long   0
655         .size   orig_int19, . - orig_int19
656
657 int19_message_prompt:
658         .asciz  "Press N to skip booting from "
659         .size   int19_message_prompt, . - int19_message_prompt
660 int19_message_dots:
661         .asciz  "..."
662         .size   int19_message_dots, . - int19_message_dots
663 int19_message_done:
664         .asciz  "\n\n"
665         .size   int19_message_done, . - int19_message_done
666         
667 /* Execute as a boot device
668  *
669  */
670 exec:   /* Set %ds = %cs */
671         pushw   %cs
672         popw    %ds
673
674         /* Preserve state of CF */
675         lahf
676
677         /* Print message as soon as possible */
678         movw    $prodstr, %si
679         xorw    %di, %di
680         call    print_message
681         movw    $exec_message_pre_install, %si
682         call    print_message
683
684         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
685         pushl   $STACK_MAGIC
686         movw    %ss, %dx
687         movw    %sp, %bp
688
689         /* Obtain a reasonably-sized temporary stack */
690         xorw    %bx, %bx
691         movw    %bx, %ss
692         movw    $0x7c00, %sp
693
694         /* Install gPXE */
695         sahf
696         pushfw
697         call    alloc_basemem
698         popfw
699         movl    image_source, %esi
700         movl    decompress_to, %edi
701         call    install_prealloc
702
703         /* Print message indicating successful installation */
704         movw    $exec_message_post_install, %si
705         xorw    %di, %di
706         call    print_message
707
708         /* Set up real-mode stack */
709         movw    %bx, %ss
710         movw    $_estack16, %sp
711
712         /* Jump to .text16 segment */
713         pushw   %ax
714         pushw   $1f
715         lret
716         .section ".text16", "awx", @progbits
717 1:      /* Call main() */
718         pushl   $main
719         pushw   %cs
720         call    prot_call
721         popl    %ecx /* discard */
722
723         /* Uninstall gPXE */
724         call    uninstall
725
726         /* Restore BIOS stack */
727         movw    %dx, %ss
728         movw    %bp, %sp
729
730         /* Check magic word on BIOS stack */
731         popl    %eax
732         cmpl    $STACK_MAGIC, %eax
733         jne     1f
734         /* BIOS stack OK: return to caller */
735         lret
736 1:      /* BIOS stack corrupt: use INT 18 */
737         int     $0x18
738         .previous
739
740 exec_message_pre_install:
741         .asciz  " starting execution..."
742         .size exec_message_pre_install, . - exec_message_pre_install
743 exec_message_post_install:
744         .asciz  "ok\n"
745         .size exec_message_post_install, . - exec_message_post_install
746
747 /* Wait for key press specified by %bl (masked by %bh)
748  *
749  * Used by init and INT19 code when prompting user.  If the specified
750  * key is pressed, it is left in the keyboard buffer.
751  *
752  * Returns with ZF set iff specified key is pressed.
753  */
754 wait_for_key:
755         /* Preserve registers */
756         pushw   %cx
757         pushw   %ax
758 1:      /* Empty the keyboard buffer before waiting for input */
759         movb    $0x01, %ah
760         int     $0x16
761         jz      2f
762         xorw    %ax, %ax
763         int     $0x16
764         jmp     1b
765 2:      /* Wait for a key press */
766         movw    $ROM_BANNER_TIMEOUT, %cx
767 3:      decw    %cx
768         js      99f             /* Exit with ZF clear */
769         /* Wait for timer tick to be updated */
770         call    wait_for_tick
771         /* Check to see if a key was pressed */
772         movb    $0x01, %ah
773         int     $0x16
774         jz      3b
775         /* Check to see if key was the specified key */
776         andb    %bh, %al
777         cmpb    %al, %bl
778         je      99f             /* Exit with ZF set */
779         /* Not the specified key: remove from buffer and stop waiting */
780         pushfw
781         xorw    %ax, %ax
782         int     $0x16
783         popfw                   /* Exit with ZF clear */
784 99:     /* Restore registers and return */
785         popw    %ax
786         popw    %cx
787         ret
788         .size wait_for_key, . - wait_for_key
789
790 /* Wait for timer tick
791  *
792  * Used by wait_for_key
793  */
794 wait_for_tick:
795         pushl   %eax
796         pushw   %fs
797         movw    $0x40, %ax
798         movw    %ax, %fs
799         movl    %fs:(0x6c), %eax
800 1:      pushf
801         sti
802         hlt
803         popf
804         cmpl    %fs:(0x6c), %eax
805         je      1b
806         popw    %fs
807         popl    %eax
808         ret
809         .size wait_for_tick, . - wait_for_tick