• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 )
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/* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
29 * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
30 * The latter is not as widely supported, but allows the use of large ROMs
31 * on some systems with crowded option ROM space.
32 */
33
34#ifdef LOAD_ROM_FROM_PCI
35#define ROM_SIZE_VALUE	_prefix_filesz_sect /* Amount to load in BIOS */
36#else
37#define ROM_SIZE_VALUE	0		/* Load amount (before compr. fixup) */
38#endif
39
40
41	.text
42	.code16
43	.arch i386
44	.section ".prefix", "ax", @progbits
45
46	.org	0x00
47romheader:
48	.word	0xAA55			/* BIOS extension signature */
49romheader_size:	.byte ROM_SIZE_VALUE	/* Size in 512-byte blocks */
50	jmp	init			/* Initialisation vector */
51checksum:
52	.byte	0, 0
53real_size:
54	.word	0
55	.org	0x16
56	.word	undiheader
57	.org	0x18
58	.word	pciheader
59	.org	0x1a
60	.word	pnpheader
61	.size romheader, . - romheader
62
63	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
64#ifndef LOAD_ROM_FROM_PCI
65	.ascii	"ADDB"
66	.long	romheader_size
67	.long	512
68	.long	0
69#endif
70	.ascii	"ADDB"
71	.long	real_size
72	.long	512
73	.long	0
74	.previous
75
76pciheader:
77	.ascii	"PCIR"			/* Signature */
78	.word	pci_vendor_id		/* Vendor identification */
79	.word	pci_device_id		/* Device identification */
80	.word	0x0000			/* Device list pointer */
81	.word	pciheader_len		/* PCI data structure length */
82	.byte	0x03			/* PCI data structure revision */
83	.byte	0x02, 0x00, 0x00	/* Class code */
84pciheader_image_length:
85	.word	ROM_SIZE_VALUE		/* Image length */
86	.word	0x0001			/* Revision level */
87	.byte	0x00			/* Code type */
88	.byte	0x80			/* Last image indicator */
89pciheader_runtime_length:
90	.word	ROM_SIZE_VALUE		/* Maximum run-time image length */
91	.word	0x0000			/* Configuration utility code header */
92	.word	0x0000			/* DMTF CLP entry point */
93	.equ pciheader_len, . - pciheader
94	.size pciheader, . - pciheader
95
96#ifndef LOAD_ROM_FROM_PCI
97	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
98	.ascii	"ADDW"
99	.long	pciheader_image_length
100	.long	512
101	.long	0
102	.ascii	"ADDW"
103	.long	pciheader_runtime_length
104	.long	512
105	.long	0
106	.previous
107#endif
108
109pnpheader:
110	.ascii	"$PnP"			/* Signature */
111	.byte	0x01			/* Structure revision */
112	.byte	( pnpheader_len	/ 16 )	/* Length (in 16 byte increments) */
113	.word	0x0000			/* Offset of next header */
114	.byte	0x00			/* Reserved */
115	.byte	0x00			/* Checksum */
116	.long	0x00000000		/* Device identifier */
117	.word	mfgstr			/* Manufacturer string */
118	.word	prodstr			/* Product name */
119	.byte	0x02			/* Device base type code */
120	.byte	0x00			/* Device sub-type code */
121	.byte	0x00			/* Device interface type code */
122	.byte	0xf4			/* Device indicator */
123	.word	0x0000			/* Boot connection vector */
124	.word	0x0000			/* Disconnect vector */
125	.word	bev_entry		/* Boot execution vector */
126	.word	0x0000			/* Reserved */
127	.word	0x0000			/* Static resource information vector*/
128	.equ pnpheader_len, . - pnpheader
129	.size pnpheader, . - pnpheader
130
131/* Manufacturer string */
132mfgstr:
133	.asciz	"http://etherboot.org"
134	.size mfgstr, . - mfgstr
135
136/* Product string
137 *
138 * Defaults to PRODUCT_SHORT_NAME.  If the ROM image is writable at
139 * initialisation time, it will be filled in to include the PCI
140 * bus:dev.fn number of the card as well.
141 */
142prodstr:
143	.ascii	PRODUCT_SHORT_NAME
144prodstr_separator:
145	.byte	0
146	.ascii	"(PCI "
147prodstr_pci_id:
148	.asciz	"xx:xx.x)"		/* Filled in by init code */
149	.size prodstr, . - prodstr
150
151	.globl	undiheader
152	.weak	undiloader
153undiheader:
154	.ascii	"UNDI"			/* Signature */
155	.byte	undiheader_len		/* Length of structure */
156	.byte	0			/* Checksum */
157	.byte	0			/* Structure revision */
158	.byte	0,1,2			/* PXE version: 2.1.0 */
159	.word	undiloader		/* Offset to loader routine */
160	.word	_data16_memsz		/* Stack segment size */
161	.word	_data16_memsz		/* Data segment size */
162	.word	_text16_memsz		/* Code segment size */
163	.ascii	"PCIR"			/* Bus type */
164	.equ undiheader_len, . - undiheader
165	.size undiheader, . - undiheader
166
167/* Initialisation (called once during POST)
168 *
169 * Determine whether or not this is a PnP system via a signature
170 * check.  If it is PnP, return to the PnP BIOS indicating that we are
171 * a boot-capable device; the BIOS will call our boot execution vector
172 * if it wants to boot us.  If it is not PnP, hook INT 19.
173 */
174init:
175	/* Preserve registers, clear direction flag, set %ds=%cs */
176	pushaw
177	pushw	%ds
178	pushw	%es
179	pushw	%fs
180	pushw	%gs
181	cld
182	pushw	%cs
183	popw	%ds
184
185	/* Shuffle some registers around.  We need %di available for
186	 * the print_xxx functions, and in a register that's
187	 * addressable from %es, so shuffle as follows:
188	 *
189	 *    %di (pointer to PnP structure) => %bx
190	 *    %bx (runtime segment address, for PCI 3.0) => %gs
191	 */
192	movw	%bx, %gs
193	movw	%di, %bx
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#ifdef LOAD_ROM_FROM_PCI
202	/* Save PCI bus:dev.fn for later use */
203	movw	%ax, pci_busdevfn
204#endif
205
206	/* Fill in product name string, if possible */
207	movw	$prodstr_pci_id, %di
208	call	print_pci_busdevfn
209	movb	$( ' ' ), prodstr_separator
210
211	/* Print segment address */
212	movb	$( ' ' ), %al
213	xorw	%di, %di
214	call	print_character
215	movw	%cs, %ax
216	call	print_hex_word
217
218	/* Check for PCI BIOS version */
219	pushl	%ebx
220	pushl	%edx
221	pushl	%edi
222	stc
223	movw	$0xb101, %ax
224	int	$0x1a
225	jc	no_pci3
226	cmpl	$PCI_SIGNATURE, %edx
227	jne	no_pci3
228	testb	%ah, %ah
229	jnz	no_pci3
230#ifdef LOAD_ROM_FROM_PCI
231	incb	pcibios_present
232#endif
233	movw	$init_message_pci, %si
234	xorw	%di, %di
235	call	print_message
236	movb	%bh, %al
237	call	print_hex_nibble
238	movb	$( '.' ), %al
239	call	print_character
240	movb	%bl, %al
241	call	print_hex_byte
242	cmpb	$3, %bh
243	jb	no_pci3
244	/* PCI >=3.0: leave %gs as-is if sane */
245	movw	%gs, %ax
246	cmpw	$0xa000, %ax	/* Insane if %gs < 0xa000 */
247	jb	pci3_insane
248	movw	%cs, %bx	/* Sane if %cs == %gs */
249	cmpw	%bx, %ax
250	je	1f
251	movzbw	romheader_size, %cx /* Sane if %cs+len <= %gs */
252	shlw	$5, %cx
253	addw	%cx, %bx
254	cmpw	%bx, %ax
255	jae	1f
256	movw	%cs, %bx	/* Sane if %gs+len <= %cs */
257	addw	%cx, %ax
258	cmpw	%bx, %ax
259	jbe	1f
260pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
261	movb	$( '!' ), %al
262	call	print_character
263	movw	%gs, %ax
264	call	print_hex_word
265no_pci3:
266	/* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
267	pushw	%cs
268	popw	%gs
2691:	popl	%edi
270	popl	%edx
271	popl	%ebx
272
273	/* Check for PnP BIOS.  Although %es:di should point to the
274	 * PnP BIOS signature on entry, some BIOSes fail to do this.
275	 */
276	movw	$( 0xf000 - 1 ), %bx
277pnp_scan:
278	incw	%bx
279	jz	no_pnp
280	movw	%bx, %es
281	cmpl	$PNP_SIGNATURE, %es:0
282	jne	pnp_scan
283	xorw	%dx, %dx
284	xorw	%si, %si
285	movzbw	%es:5, %cx
2861:	es lodsb
287	addb	%al, %dl
288	loop	1b
289	jnz	pnp_scan
290	/* Is PnP: print PnP message */
291	movw	$init_message_pnp, %si
292	xorw	%di, %di
293	call	print_message
294	/* Check for BBS */
295	pushw	%es:0x1b	/* Real-mode data segment */
296	pushw	%ds		/* &(bbs_version) */
297	pushw	$bbs_version
298	pushw	$PNP_GET_BBS_VERSION
299	lcall	*%es:0xd
300	addw	$8, %sp
301	testw	%ax, %ax
302	je	got_bbs
303no_pnp:	/* Not PnP-compliant - therefore cannot be BBS-compliant */
304no_bbs:	/* Not BBS-compliant - must hook INT 19 */
305	movw	$init_message_int19, %si
306	xorw	%di, %di
307	call	print_message
308	xorw	%ax, %ax
309	movw	%ax, %es
310	pushl	%es:( 0x19 * 4 )
311	popl	orig_int19
312	pushw	%gs /* %gs contains runtime %cs */
313	pushw	$int19_entry
314	popl	%es:( 0x19 * 4 )
315	jmp	bbs_done
316got_bbs: /* BBS compliant - no need to hook INT 19 */
317	movw	$init_message_bbs, %si
318	xorw	%di, %di
319	call	print_message
320bbs_done:
321
322	/* Check for PMM */
323	movw	$( 0xe000 - 1 ), %bx
324pmm_scan:
325	incw	%bx
326	jz	no_pmm
327	movw	%bx, %es
328	cmpl	$PMM_SIGNATURE, %es:0
329	jne	pmm_scan
330	xorw	%dx, %dx
331	xorw	%si, %si
332	movzbw	%es:5, %cx
3331:	es lodsb
334	addb	%al, %dl
335	loop	1b
336	jnz	pmm_scan
337	/* PMM found: print PMM message */
338	movw	$init_message_pmm, %si
339	xorw	%di, %di
340	call	print_message
341	/* We have PMM and so a 1kB stack: preserve upper register halves */
342	pushal
343	/* Calculate required allocation size in %esi */
344	movzwl	real_size, %eax
345	shll	$9, %eax
346	addl	$_textdata_memsz, %eax
347	orw	$0xffff, %ax	/* Ensure allocation size is at least 64kB */
348	bsrl	%eax, %ecx
349	subw	$15, %cx	/* Round up and convert to 64kB count */
350	movw	$1, %si
351	shlw	%cl, %si
352pmm_loop:
353	/* Try to allocate block via PMM */
354	pushw	$0x0006		/* Aligned, extended memory */
355	pushl	$0xffffffff	/* No handle */
356	movzwl	%si, %eax
357	shll	$12, %eax
358	pushl	%eax		/* Allocation size in paragraphs */
359	pushw	$PMM_ALLOCATE
360	lcall	*%es:7
361	addw	$12, %sp
362	/* Abort if allocation fails */
363	testw	%dx, %dx	/* %ax==0 even on success, since align>=64kB */
364	jz	pmm_fail
365	/* If block has A20==1, free block and try again with twice
366	 * the allocation size (and hence alignment).
367	 */
368	testw	$0x0010, %dx
369	jz	got_pmm
370	pushw	%dx
371	pushw	$0
372	pushw	$PMM_DEALLOCATE
373	lcall	*%es:7
374	addw	$6, %sp
375	addw	%si, %si
376	jmp	pmm_loop
377got_pmm: /* PMM allocation succeeded */
378	movw	%dx, ( image_source + 2 )
379	movw	%dx, %ax
380	xorw	%di, %di
381	call	print_hex_word
382	movb	$( '@' ), %al
383	call	print_character
384	movw	%si, %ax
385	call	print_hex_byte
386pmm_copy:
387	/* Copy ROM to PMM block */
388	xorw	%ax, %ax
389	movw	%ax, %es
390	movl	image_source, %edi
391	xorl	%esi, %esi
392	movzbl	romheader_size, %ecx
393	shll	$9, %ecx
394	addr32 rep movsb	/* PMM presence implies flat real mode */
395	movl	%edi, decompress_to
396	/* Shrink ROM */
397	movb	$_prefix_memsz_sect, romheader_size
398#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
399	jmp	pmm_done
400pmm_fail:
401	/* Print marker and copy ourselves to high memory */
402	movl	$HIGHMEM_LOADPOINT, image_source
403	xorw	%di, %di
404	movb	$( '!' ), %al
405	call	print_character
406	jmp	pmm_copy
407pmm_done:
408#else
409pmm_fail:
410#endif
411	/* Restore upper register halves */
412	popal
413#if defined(LOAD_ROM_FROM_PCI)
414	call	load_from_pci
415	jc	load_err
416	jmp	load_ok
417no_pmm:
418	/* Cannot continue without PMM - print error message */
419	xorw	%di, %di
420	movw	$init_message_no_pmm, %si
421	call	print_message
422load_err:
423	/* Wait for five seconds to let user see message */
424	movw	$90, %cx
4251:	call	wait_for_tick
426	loop	1b
427	/* Mark environment as invalid and return */
428	movl	$0, decompress_to
429	jmp	out
430
431load_ok:
432#else
433no_pmm:
434#endif
435	/* Update checksum */
436	xorw	%bx, %bx
437	xorw	%si, %si
438	movzbw	romheader_size, %cx
439	shlw	$9, %cx
4401:	lodsb
441	addb	%al, %bl
442	loop	1b
443	subb	%bl, checksum
444
445	/* Copy self to option ROM space.  Required for PCI3.0, which
446	 * loads us to a temporary location in low memory.  Will be a
447	 * no-op for lower PCI versions.
448	 */
449	movb	$( ' ' ), %al
450	xorw	%di, %di
451	call	print_character
452	movw	%gs, %ax
453	call	print_hex_word
454	movzbw	romheader_size, %cx
455	shlw	$9, %cx
456	movw	%ax, %es
457	xorw	%si, %si
458	xorw	%di, %di
459	cs rep	movsb
460
461	/* Prompt for POST-time shell */
462	movw	$init_message_prompt, %si
463	xorw	%di, %di
464	call	print_message
465	movw	$prodstr, %si
466	call	print_message
467	movw	$init_message_dots, %si
468	call	print_message
469	/* Wait for Ctrl-B */
470	movw	$0xff02, %bx
471	call	wait_for_key
472	/* Clear prompt */
473	pushf
474	xorw	%di, %di
475	call	print_kill_line
476	movw	$init_message_done, %si
477	call	print_message
478	popf
479	jnz	out
480	/* Ctrl-B was pressed: invoke gPXE.  The keypress will be
481	 * picked up by the initial shell prompt, and we will drop
482	 * into a shell.
483	 */
484	pushw	%cs
485	call	exec
486out:
487	/* Restore registers */
488	popw	%gs
489	popw	%fs
490	popw	%es
491	popw	%ds
492	popaw
493
494	/* Indicate boot capability to PnP BIOS, if present */
495	movw	$0x20, %ax
496	lret
497	.size init, . - init
498
499/*
500 * Note to hardware vendors:
501 *
502 * If you wish to brand this boot ROM, please do so by defining the
503 * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
504 *
505 * While nothing in the GPL prevents you from removing all references
506 * to gPXE or http://etherboot.org, we prefer you not to do so.
507 *
508 * If you have an OEM-mandated branding requirement that cannot be
509 * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
510 * please contact us.
511 *
512 * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
513 *   bypassing the spirit of this request! ]
514 */
515init_message:
516	.ascii	"\n"
517	.ascii	PRODUCT_NAME
518	.ascii	"\n"
519	.asciz	"gPXE (http://etherboot.org) - "
520	.size	init_message, . - init_message
521init_message_pci:
522	.asciz	" PCI"
523	.size	init_message_pci, . - init_message_pci
524init_message_pnp:
525	.asciz	" PnP"
526	.size	init_message_pnp, . - init_message_pnp
527init_message_bbs:
528	.asciz	" BBS"
529	.size	init_message_bbs, . - init_message_bbs
530init_message_pmm:
531	.asciz	" PMM"
532	.size	init_message_pmm, . - init_message_pmm
533#ifdef LOAD_ROM_FROM_PCI
534init_message_no_pmm:
535	.asciz	"\nPMM required but not present!\n"
536	.size	init_message_no_pmm, . - init_message_no_pmm
537#endif
538init_message_int19:
539	.asciz	" INT19"
540	.size	init_message_int19, . - init_message_int19
541init_message_prompt:
542	.asciz	"\nPress Ctrl-B to configure "
543	.size	init_message_prompt, . - init_message_prompt
544init_message_dots:
545	.asciz	"..."
546	.size	init_message_dots, . - init_message_dots
547init_message_done:
548	.asciz	"\n\n"
549	.size	init_message_done, . - init_message_done
550
551/* ROM image location
552 *
553 * May be either within option ROM space, or within PMM-allocated block.
554 */
555	.globl	image_source
556image_source:
557	.long	0
558	.size	image_source, . - image_source
559
560/* Temporary decompression area
561 *
562 * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
563 * If a PCI ROM load fails, this will be set to zero.
564 */
565	.globl	decompress_to
566decompress_to:
567	.long	HIGHMEM_LOADPOINT
568	.size	decompress_to, . - decompress_to
569
570#ifdef LOAD_ROM_FROM_PCI
571
572/* Set if the PCI BIOS is present, even <3.0 */
573pcibios_present:
574	.byte	0
575	.byte	0		/* for alignment */
576	.size	pcibios_present, . - pcibios_present
577
578/* PCI bus:device.function word
579 *
580 * Filled in by init in the .xrom case, so the remainder of the ROM
581 * can be located.
582 */
583pci_busdevfn:
584	.word	0
585	.size	pci_busdevfn, . - pci_busdevfn
586
587#endif
588
589/* BBS version
590 *
591 * Filled in by BBS BIOS.  We ignore the value.
592 */
593bbs_version:
594	.word	0
595	.size	bbs_version, . - bbs_version
596
597/* Boot Execution Vector entry point
598 *
599 * Called by the PnP BIOS when it wants to boot us.
600 */
601bev_entry:
602	pushw	%cs
603	call	exec
604	lret
605	.size	bev_entry, . - bev_entry
606
607
608#ifdef LOAD_ROM_FROM_PCI
609
610#define PCI_ROM_ADDRESS		0x30	/* Bits 31:11 address, 10:1 reserved */
611#define PCI_ROM_ADDRESS_ENABLE	 0x00000001
612#define PCI_ROM_ADDRESS_MASK	 0xfffff800
613
614#define PCIBIOS_READ_WORD	0xb109
615#define PCIBIOS_READ_DWORD	0xb10a
616#define PCIBIOS_WRITE_WORD	0xb10c
617#define PCIBIOS_WRITE_DWORD	0xb10d
618
619/* Determine size of PCI BAR
620 *
621 *  %bx : PCI bus:dev.fn to probe
622 *  %di : Address of BAR to find size of
623 * %edx : Mask of address bits within BAR
624 *
625 * %ecx : Size for a memory resource,
626 *	  1 for an I/O resource (bit 0 set).
627 *   CF : Set on error or nonexistent device (all-ones read)
628 *
629 * All other registers saved.
630 */
631pci_bar_size:
632	/* Save registers */
633	pushw	%ax
634	pushl	%esi
635	pushl	%edx
636
637	/* Read current BAR value */
638	movw	$PCIBIOS_READ_DWORD, %ax
639	int	$0x1a
640
641	/* Check for device existence and save it */
642	testb	$1, %cl		/* I/O bit? */
643	jz	1f
644	andl	$1, %ecx	/* If so, exit with %ecx = 1 */
645	jmp	99f
6461:	notl	%ecx
647	testl	%ecx, %ecx	/* Set ZF iff %ecx was all-ones */
648	notl	%ecx
649	jnz	1f
650	stc			/* All ones - exit with CF set */
651	jmp	99f
6521:	movl	%ecx, %esi	/* Save in %esi */
653
654	/* Write all ones to BAR */
655	movl	%edx, %ecx
656	movw	$PCIBIOS_WRITE_DWORD, %ax
657	int	$0x1a
658
659	/* Read back BAR */
660	movw	$PCIBIOS_READ_DWORD, %ax
661	int	$0x1a
662
663	/* Find decode size from least set bit in mask BAR */
664	bsfl	%ecx, %ecx	/* Find least set bit, log2(decode size) */
665	jz	1f		/* Mask BAR should not be zero */
666	xorl	%edx, %edx
667	incl	%edx
668	shll	%cl, %edx	/* %edx = decode size */
669	jmp	2f
6701:	xorl	%edx, %edx	/* Return zero size for mask BAR zero */
671
672	/* Restore old BAR value */
6732:	movl	%esi, %ecx
674	movw	$PCIBIOS_WRITE_DWORD, %ax
675	int	$0x1a
676
677	movl	%edx, %ecx	/* Return size in %ecx */
678
679	/* Restore registers and return */
68099:	popl	%edx
681	popl	%esi
682	popw	%ax
683	ret
684
685	.size	pci_bar_size, . - pci_bar_size
686
687/* PCI ROM loader
688 *
689 * Called from init in the .xrom case to load the non-prefix code
690 * using the PCI ROM BAR.
691 *
692 * Returns with carry flag set on error. All registers saved.
693 */
694load_from_pci:
695	/*
696	 * Use PCI BIOS access to config space. The calls take
697	 *
698	 *   %ah : 0xb1		%al : function
699	 *   %bx : bus/dev/fn
700	 *   %di : config space address
701	 *  %ecx : value to write (for writes)
702	 *
703	 *  %ecx : value read (for reads)
704	 *   %ah : return code
705	 *    CF : error indication
706	 *
707	 * All registers not used for return are preserved.
708	 */
709
710	/* Save registers and set up %es for big real mode */
711	pushal
712	pushw	%es
713	xorw	%ax, %ax
714	movw	%ax, %es
715
716	/* Check PCI BIOS presence */
717	cmpb	$0, pcibios_present
718	jz	err_pcibios
719
720	/* Load existing PCI ROM BAR */
721	movw	$PCIBIOS_READ_DWORD, %ax
722	movw	pci_busdevfn, %bx
723	movw	$PCI_ROM_ADDRESS, %di
724	int	$0x1a
725
726	/* Maybe it's already enabled? */
727	testb	$PCI_ROM_ADDRESS_ENABLE, %cl
728	jz	1f
729	movb	$1, %dl		/* Flag indicating no deinit required */
730	movl	%ecx, %ebp
731	jmp	check_rom
732
733	/* Determine PCI BAR decode size */
7341:	movl	$PCI_ROM_ADDRESS_MASK, %edx
735	call	pci_bar_size	/* Returns decode size in %ecx */
736	jc	err_size_insane	/* CF => no ROM BAR, %ecx == ffffffff */
737
738	/* Check sanity of decode size */
739	xorl	%eax, %eax
740	movw	real_size, %ax
741	shll	$9, %eax	/* %eax = ROM size */
742	cmpl	%ecx, %eax
743	ja	err_size_insane	/* Insane if decode size < ROM size */
744	cmpl	$0x100000, %ecx
745	jae	err_size_insane	/* Insane if decode size >= 1MB */
746
747	/* Find a place to map the BAR
748	 * In theory we should examine e820 and all PCI BARs to find a
749	 * free region. However, we run at POST when e820 may not be
750	 * available, and memory reads of an unmapped location are
751	 * de facto standardized to return all-ones. Thus, we can get
752	 * away with searching high memory (0xf0000000 and up) on
753	 * multiples of the ROM BAR decode size for a sufficiently
754	 * large all-ones region.
755	 */
756	movl	%ecx, %edx	/* Save ROM BAR size in %edx */
757	movl	$0xf0000000, %ebp
758	xorl	%eax, %eax
759	notl	%eax		/* %eax = all ones */
760bar_search:
761	movl	%ebp, %edi
762	movl	%edx, %ecx
763	shrl	$2, %ecx
764	addr32 repe scasl	/* Scan %es:edi for anything not all-ones */
765	jz	bar_found
766	addl	%edx, %ebp
767	testl	$0x80000000, %ebp
768	jz	err_no_bar
769	jmp	bar_search
770
771bar_found:
772	movl	%edi, %ebp
773	/* Save current BAR value on stack to restore later */
774	movw	$PCIBIOS_READ_DWORD, %ax
775	movw	$PCI_ROM_ADDRESS, %di
776	int	$0x1a
777	pushl	%ecx
778
779	/* Map the ROM */
780	movw	$PCIBIOS_WRITE_DWORD, %ax
781	movl	%ebp, %ecx
782	orb	$PCI_ROM_ADDRESS_ENABLE, %cl
783	int	$0x1a
784
785	xorb	%dl, %dl	/* %dl = 0 : ROM was not already mapped */
786check_rom:
787	/* Check and copy ROM - enter with %dl set to skip unmapping,
788	 * %ebp set to mapped ROM BAR address.
789	 * We check up to prodstr_separator for equality, since anything past
790	 * that may have been modified. Since our check includes the checksum
791	 * byte over the whole ROM stub, that should be sufficient.
792	 */
793	xorb	%dh, %dh	/* %dh = 0 : ROM did not fail integrity check */
794
795	/* Verify ROM integrity */
796	xorl	%esi, %esi
797	movl	%ebp, %edi
798	movl	$prodstr_separator, %ecx
799	addr32 repe cmpsb
800	jz	copy_rom
801	incb	%dh		/* ROM failed integrity check */
802	movl	%ecx, %ebp	/* Save number of bytes left */
803	jmp	skip_load
804
805copy_rom:
806	/* Print BAR address and indicate whether we mapped it ourselves */
807	movb	$( ' ' ), %al
808	xorw	%di, %di
809	call	print_character
810	movl	%ebp, %eax
811	call	print_hex_dword
812	movb	$( '-' ), %al	/* '-' for self-mapped */
813	subb	%dl, %al
814	subb	%dl, %al	/* '+' = '-' - 2 for BIOS-mapped */
815	call	print_character
816
817	/* Copy ROM at %ebp to PMM or highmem block */
818	movl	%ebp, %esi
819	movl	image_source, %edi
820	movzwl	real_size, %ecx
821	shll	$9, %ecx
822	addr32 es rep movsb
823	movl	%edi, decompress_to
824skip_load:
825	testb	%dl, %dl	/* Was ROM already mapped? */
826	jnz	skip_unmap
827
828	/* Unmap the ROM by restoring old ROM BAR */
829	movw	$PCIBIOS_WRITE_DWORD, %ax
830	movw	$PCI_ROM_ADDRESS, %di
831	popl	%ecx
832	int	$0x1a
833
834skip_unmap:
835	/* Error handling */
836	testb	%dh, %dh
837	jnz	err_rom_invalid
838	clc
839	jmp	99f
840
841err_pcibios:			/* No PCI BIOS available */
842	movw	$load_message_no_pcibios, %si
843	xorl	%eax, %eax	/* "error code" is zero */
844	jmp	1f
845err_size_insane:		/* BAR has size (%ecx) that is insane */
846	movw	$load_message_size_insane, %si
847	movl	%ecx, %eax
848	jmp	1f
849err_no_bar:			/* No space of sufficient size (%edx) found */
850	movw	$load_message_no_bar, %si
851	movl	%edx, %eax
852	jmp	1f
853err_rom_invalid:		/* Loaded ROM does not match (%ebp bytes left) */
854	movw	$load_message_rom_invalid, %si
855	movzbl	romheader_size, %eax
856	shll	$9, %eax
857	subl	%ebp, %eax
858	decl	%eax		/* %eax is now byte index of failure */
859
8601:	/* Error handler - print message at %si and dword in %eax */
861	xorw	%di, %di
862	call	print_message
863	call	print_hex_dword
864	stc
86599:	popw	%es
866	popal
867	ret
868
869	.size	load_from_pci, . - load_from_pci
870
871load_message_no_pcibios:
872	.asciz	"\nNo PCI BIOS found! "
873	.size	load_message_no_pcibios, . - load_message_no_pcibios
874
875load_message_size_insane:
876	.asciz	"\nROM resource has invalid size "
877	.size	load_message_size_insane, . - load_message_size_insane
878
879load_message_no_bar:
880	.asciz	"\nNo memory hole of sufficient size "
881	.size	load_message_no_bar, . - load_message_no_bar
882
883load_message_rom_invalid:
884	.asciz	"\nLoaded ROM is invalid at "
885	.size	load_message_rom_invalid, . - load_message_rom_invalid
886
887#endif /* LOAD_ROM_FROM_PCI */
888
889
890/* INT19 entry point
891 *
892 * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
893 * attempt to return via the original INT 19 vector (if we were able
894 * to store it).
895 */
896int19_entry:
897	pushw	%cs
898	popw	%ds
899	/* Prompt user to press B to boot */
900	movw	$int19_message_prompt, %si
901	xorw	%di, %di
902	call	print_message
903	movw	$prodstr, %si
904	call	print_message
905	movw	$int19_message_dots, %si
906	call	print_message
907	movw	$0xdf4e, %bx
908	call	wait_for_key
909	pushf
910	xorw	%di, %di
911	call	print_kill_line
912	movw	$int19_message_done, %si
913	call	print_message
914	popf
915	jz	1f
916	/* Leave keypress in buffer and start gPXE.  The keypress will
917	 * cause the usual initial Ctrl-B prompt to be skipped.
918	 */
919	pushw	%cs
920	call	exec
9211:	/* Try to call original INT 19 vector */
922	movl	%cs:orig_int19, %eax
923	testl	%eax, %eax
924	je	2f
925	ljmp	*%cs:orig_int19
9262:	/* No chained vector: issue INT 18 as a last resort */
927	int	$0x18
928	.size	int19_entry, . - int19_entry
929orig_int19:
930	.long	0
931	.size	orig_int19, . - orig_int19
932
933int19_message_prompt:
934	.asciz	"Press N to skip booting from "
935	.size	int19_message_prompt, . - int19_message_prompt
936int19_message_dots:
937	.asciz	"..."
938	.size	int19_message_dots, . - int19_message_dots
939int19_message_done:
940	.asciz	"\n\n"
941	.size	int19_message_done, . - int19_message_done
942
943/* Execute as a boot device
944 *
945 */
946exec:	/* Set %ds = %cs */
947	pushw	%cs
948	popw	%ds
949
950#ifdef LOAD_ROM_FROM_PCI
951	/* Don't execute if load was invalid */
952	cmpl	$0, decompress_to
953	jne	1f
954	lret
9551:
956#endif
957
958	/* Print message as soon as possible */
959	movw	$prodstr, %si
960	xorw	%di, %di
961	call	print_message
962	movw	$exec_message, %si
963	call	print_message
964
965	/* Store magic word on BIOS stack and remember BIOS %ss:sp */
966	pushl	$STACK_MAGIC
967	movw	%ss, %dx
968	movw	%sp, %bp
969
970	/* Obtain a reasonably-sized temporary stack */
971	xorw	%ax, %ax
972	movw	%ax, %ss
973	movw	$0x7c00, %sp
974
975	/* Install gPXE */
976	movl	image_source, %esi
977	movl	decompress_to, %edi
978	call	alloc_basemem
979	call	install_prealloc
980
981	/* Set up real-mode stack */
982	movw	%bx, %ss
983	movw	$_estack16, %sp
984
985	/* Jump to .text16 segment */
986	pushw	%ax
987	pushw	$1f
988	lret
989	.section ".text16", "awx", @progbits
9901:	/* Call main() */
991	pushl	$main
992	pushw	%cs
993	call	prot_call
994	popl	%ecx /* discard */
995
996	/* Uninstall gPXE */
997	call	uninstall
998
999	/* Restore BIOS stack */
1000	movw	%dx, %ss
1001	movw	%bp, %sp
1002
1003	/* Check magic word on BIOS stack */
1004	popl	%eax
1005	cmpl	$STACK_MAGIC, %eax
1006	jne	1f
1007	/* BIOS stack OK: return to caller */
1008	lret
10091:	/* BIOS stack corrupt: use INT 18 */
1010	int	$0x18
1011	.previous
1012
1013exec_message:
1014	.asciz	" starting execution\n"
1015	.size exec_message, . - exec_message
1016
1017/* Wait for key press specified by %bl (masked by %bh)
1018 *
1019 * Used by init and INT19 code when prompting user.  If the specified
1020 * key is pressed, it is left in the keyboard buffer.
1021 *
1022 * Returns with ZF set iff specified key is pressed.
1023 */
1024wait_for_key:
1025	/* Preserve registers */
1026	pushw	%cx
1027	pushw	%ax
10281:	/* Empty the keyboard buffer before waiting for input */
1029	movb	$0x01, %ah
1030	int	$0x16
1031	jz	2f
1032	xorw	%ax, %ax
1033	int	$0x16
1034	jmp	1b
10352:	/* Wait for a key press */
1036	movw	$ROM_BANNER_TIMEOUT, %cx
10373:	decw	%cx
1038	js	99f		/* Exit with ZF clear */
1039	/* Wait for timer tick to be updated */
1040	call	wait_for_tick
1041	/* Check to see if a key was pressed */
1042	movb	$0x01, %ah
1043	int	$0x16
1044	jz	3b
1045	/* Check to see if key was the specified key */
1046	andb	%bh, %al
1047	cmpb	%al, %bl
1048	je	99f		/* Exit with ZF set */
1049	/* Not the specified key: remove from buffer and stop waiting */
1050	pushfw
1051	xorw	%ax, %ax
1052	int	$0x16
1053	popfw			/* Exit with ZF clear */
105499:	/* Restore registers and return */
1055	popw	%ax
1056	popw	%cx
1057	ret
1058	.size wait_for_key, . - wait_for_key
1059
1060/* Wait for timer tick
1061 *
1062 * Used by wait_for_key
1063 */
1064wait_for_tick:
1065	pushl	%eax
1066	pushw	%fs
1067	movw	$0x40, %ax
1068	movw	%ax, %fs
1069	movl	%fs:(0x6c), %eax
10701:	pushf
1071	sti
1072	hlt
1073	popf
1074	cmpl	%fs:(0x6c), %eax
1075	je	1b
1076	popw	%fs
1077	popl	%eax
1078	ret
1079	.size wait_for_tick, . - wait_for_tick
1080