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