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