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