[romprefix] Split PMM allocations for image source and decompression area
[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         pushw   %cs
411         call    exec
412 2:
413         /* Restore registers */
414         popw    %gs
415         popw    %fs
416         popw    %es
417         popw    %ds
418         popaw
419
420         /* Indicate boot capability to PnP BIOS, if present */
421         movw    $0x20, %ax
422         lret
423         .size init, . - init
424
425 /* Attempt to find or allocate PMM block
426  *
427  * Parameters:
428  *  %ecx : size of block to allocate, in paragraphs
429  *  %ebx : PMM handle base
430  *  %bp : routine to check acceptability of found blocks
431  *  %es:0000 : PMM structure
432  * Returns:
433  *  %ebx : PMM handle
434  *  %esi : allocated block address, or zero (with CF set) if allocation failed
435  */
436 get_pmm:
437         /* Preserve registers */
438         pushl   %eax
439         pushw   %di
440         movw    $' ', %di
441 get_pmm_find:
442         /* Try to find existing block */
443         pushl   %ebx            /* PMM handle */
444         pushw   $PMM_FIND
445         lcall   *%es:7
446         addw    $6, %sp
447         pushw   %dx
448         pushw   %ax
449         popl    %esi
450         testl   %esi, %esi
451         jz      get_pmm_allocate
452         /* Block found - check acceptability */
453         call    *%bp
454         jnc     get_pmm_done
455         /* Block not acceptable - increment handle and retry */
456         incl    %ebx
457         jmp     get_pmm_find
458 get_pmm_allocate:
459         /* Block not found - try to allocate new block */
460         pushw   $0x0002         /* Extended memory */
461         pushl   %ebx            /* PMM handle */
462         pushl   %ecx            /* Length */
463         pushw   $PMM_ALLOCATE
464         lcall   *%es:7
465         addw    $12, %sp
466         pushw   %dx
467         pushw   %ax
468         popl    %esi
469         movw    $'+', %di       /* Indicate allocation attempt */
470         testl   %esi, %esi
471         jnz     get_pmm_done
472         stc
473 get_pmm_done:
474         /* Print block address */
475         pushfw
476         movw    %di, %ax
477         xorw    %di, %di
478         call    print_character
479         movl    %esi, %eax
480         call    print_hex_dword
481         popfw
482         /* Restore registers and return */
483         popw    %di
484         popl    %eax
485         ret
486         .size   get_pmm, . - get_pmm
487
488         /* Check acceptability of image source block */
489 get_pmm_image_source:
490         pushw   %es
491         xorw    %ax, %ax
492         movw    %ax, %es
493         movl    build_id, %eax
494         cmpl    %es:build_id(%esi), %eax
495         je      1f
496         stc
497 1:      popw    %es
498         ret
499         .size   get_pmm_image_source, . - get_pmm_image_source
500
501         /* Check acceptability of decompression block */
502 get_pmm_decompress_to:
503         clc
504         ret
505         .size   get_pmm_decompress_to, . - get_pmm_decompress_to
506
507 /*
508  * Note to hardware vendors:
509  *
510  * If you wish to brand this boot ROM, please do so by defining the
511  * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
512  *
513  * While nothing in the GPL prevents you from removing all references
514  * to gPXE or http://etherboot.org, we prefer you not to do so.
515  *
516  * If you have an OEM-mandated branding requirement that cannot be
517  * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
518  * please contact us.
519  *
520  * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
521  *   bypassing the spirit of this request! ]
522  */
523 init_message:
524         .ascii  "\n"
525         .ascii  PRODUCT_NAME
526         .ascii  "\n"
527         .asciz  "gPXE (http://etherboot.org) - "
528         .size   init_message, . - init_message
529 init_message_pci:
530         .asciz  " PCI"
531         .size   init_message_pci, . - init_message_pci
532 init_message_pnp:
533         .asciz  " PnP"
534         .size   init_message_pnp, . - init_message_pnp
535 init_message_bbs:
536         .asciz  " BBS"
537         .size   init_message_bbs, . - init_message_bbs
538 init_message_pmm:
539         .asciz  " PMM"
540         .size   init_message_pmm, . - init_message_pmm
541 init_message_int19:
542         .asciz  " INT19"
543         .size   init_message_int19, . - init_message_int19
544 init_message_prompt:
545         .asciz  "\nPress Ctrl-B to configure "
546         .size   init_message_prompt, . - init_message_prompt
547 init_message_dots:
548         .asciz  "..."
549         .size   init_message_dots, . - init_message_dots
550 init_message_done:
551         .asciz  "\n\n"
552         .size   init_message_done, . - init_message_done
553
554 /* Image source area
555  *
556  * May be either zero (indicating to use option ROM space as source),
557  * or within a PMM-allocated block.
558  */
559         .globl  image_source
560 image_source:
561         .long   0
562         .size   image_source, . - image_source
563
564 /* Shrunk ROM size (in 512-byte sectors)
565  *
566  */
567 shrunk_rom_size:
568         .byte   0
569         .size   shrunk_rom_size, . - shrunk_rom_size
570         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
571         .ascii  "ADHB"
572         .long   shrunk_rom_size
573         .long   512
574         .long   0
575         .previous
576
577 /* Temporary decompression area
578  *
579  * May be either zero (indicating to use default decompression area in
580  * high memory), or within a PMM-allocated block.
581  */
582         .globl  decompress_to
583 decompress_to:
584         .long   0
585         .size   decompress_to, . - decompress_to
586
587 /* BBS version
588  *
589  * Filled in by BBS BIOS.  We ignore the value.
590  */
591 bbs_version:
592         .word   0
593         .size   bbs_version, . - bbs_version
594
595 /* Boot Execution Vector entry point
596  *
597  * Called by the PnP BIOS when it wants to boot us.
598  */
599 bev_entry:
600         pushw   %cs
601         call    exec
602         lret
603         .size   bev_entry, . - bev_entry
604
605 /* INT19 entry point
606  *
607  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
608  * attempt to return via the original INT 19 vector (if we were able
609  * to store it).
610  */
611 int19_entry:
612         pushw   %cs
613         popw    %ds
614         /* Prompt user to press B to boot */
615         movw    $int19_message_prompt, %si
616         xorw    %di, %di
617         call    print_message
618         movw    $prodstr, %si
619         call    print_message
620         movw    $int19_message_dots, %si
621         call    print_message
622         movw    $0xdf4e, %bx
623         call    wait_for_key
624         pushf
625         xorw    %di, %di
626         call    print_kill_line
627         movw    $int19_message_done, %si
628         call    print_message
629         popf
630         jz      1f
631         /* Leave keypress in buffer and start gPXE.  The keypress will
632          * cause the usual initial Ctrl-B prompt to be skipped.
633          */
634         pushw   %cs
635         call    exec
636 1:      /* Try to call original INT 19 vector */
637         movl    %cs:orig_int19, %eax
638         testl   %eax, %eax
639         je      2f
640         ljmp    *%cs:orig_int19
641 2:      /* No chained vector: issue INT 18 as a last resort */
642         int     $0x18
643         .size   int19_entry, . - int19_entry
644 orig_int19:
645         .long   0
646         .size   orig_int19, . - orig_int19
647
648 int19_message_prompt:
649         .asciz  "Press N to skip booting from "
650         .size   int19_message_prompt, . - int19_message_prompt
651 int19_message_dots:
652         .asciz  "..."
653         .size   int19_message_dots, . - int19_message_dots
654 int19_message_done:
655         .asciz  "\n\n"
656         .size   int19_message_done, . - int19_message_done
657         
658 /* Execute as a boot device
659  *
660  */
661 exec:   /* Set %ds = %cs */
662         pushw   %cs
663         popw    %ds
664
665         /* Print message as soon as possible */
666         movw    $prodstr, %si
667         xorw    %di, %di
668         call    print_message
669         movw    $exec_message_pre_install, %si
670         call    print_message
671
672         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
673         pushl   $STACK_MAGIC
674         movw    %ss, %dx
675         movw    %sp, %bp
676
677         /* Obtain a reasonably-sized temporary stack */
678         xorw    %ax, %ax
679         movw    %ax, %ss
680         movw    $0x7c00, %sp
681
682         /* Install gPXE */
683         movl    image_source, %esi
684         movl    decompress_to, %edi
685         call    alloc_basemem
686         call    install_prealloc
687
688         /* Print message indicating successful installation */
689         movw    $exec_message_post_install, %si
690         xorw    %di, %di
691         call    print_message
692
693         /* Set up real-mode stack */
694         movw    %bx, %ss
695         movw    $_estack16, %sp
696
697         /* Jump to .text16 segment */
698         pushw   %ax
699         pushw   $1f
700         lret
701         .section ".text16", "awx", @progbits
702 1:      /* Call main() */
703         pushl   $main
704         pushw   %cs
705         call    prot_call
706         popl    %ecx /* discard */
707
708         /* Uninstall gPXE */
709         call    uninstall
710
711         /* Restore BIOS stack */
712         movw    %dx, %ss
713         movw    %bp, %sp
714
715         /* Check magic word on BIOS stack */
716         popl    %eax
717         cmpl    $STACK_MAGIC, %eax
718         jne     1f
719         /* BIOS stack OK: return to caller */
720         lret
721 1:      /* BIOS stack corrupt: use INT 18 */
722         int     $0x18
723         .previous
724
725 exec_message_pre_install:
726         .asciz  " starting execution..."
727         .size exec_message_pre_install, . - exec_message_pre_install
728 exec_message_post_install:
729         .asciz  "ok\n"
730         .size exec_message_post_install, . - exec_message_post_install
731
732 /* Wait for key press specified by %bl (masked by %bh)
733  *
734  * Used by init and INT19 code when prompting user.  If the specified
735  * key is pressed, it is left in the keyboard buffer.
736  *
737  * Returns with ZF set iff specified key is pressed.
738  */
739 wait_for_key:
740         /* Preserve registers */
741         pushw   %cx
742         pushw   %ax
743 1:      /* Empty the keyboard buffer before waiting for input */
744         movb    $0x01, %ah
745         int     $0x16
746         jz      2f
747         xorw    %ax, %ax
748         int     $0x16
749         jmp     1b
750 2:      /* Wait for a key press */
751         movw    $ROM_BANNER_TIMEOUT, %cx
752 3:      decw    %cx
753         js      99f             /* Exit with ZF clear */
754         /* Wait for timer tick to be updated */
755         call    wait_for_tick
756         /* Check to see if a key was pressed */
757         movb    $0x01, %ah
758         int     $0x16
759         jz      3b
760         /* Check to see if key was the specified key */
761         andb    %bh, %al
762         cmpb    %al, %bl
763         je      99f             /* Exit with ZF set */
764         /* Not the specified key: remove from buffer and stop waiting */
765         pushfw
766         xorw    %ax, %ax
767         int     $0x16
768         popfw                   /* Exit with ZF clear */
769 99:     /* Restore registers and return */
770         popw    %ax
771         popw    %cx
772         ret
773         .size wait_for_key, . - wait_for_key
774
775 /* Wait for timer tick
776  *
777  * Used by wait_for_key
778  */
779 wait_for_tick:
780         pushl   %eax
781         pushw   %fs
782         movw    $0x40, %ax
783         movw    %ax, %fs
784         movl    %fs:(0x6c), %eax
785 1:      pushf
786         sti
787         hlt
788         popf
789         cmpl    %fs:(0x6c), %eax
790         je      1b
791         popw    %fs
792         popl    %eax
793         ret
794         .size wait_for_tick, . - wait_for_tick