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