• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1FILE_LICENCE ( GPL2_OR_LATER )
2
3#define PXENV_UNDI_SHUTDOWN		0x0005
4#define	PXENV_UNDI_GET_NIC_TYPE		0x0012
5#define PXENV_UNDI_GET_IFACE_INFO	0x0013
6#define	PXENV_STOP_UNDI			0x0015
7#define PXENV_UNLOAD_STACK		0x0070
8
9#define PXE_HACK_EB54			0x0001
10
11	.text
12	.arch i386
13	.org 0
14	.code16
15
16#include <undi.h>
17
18#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
19#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
20#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
21
22/*****************************************************************************
23 * Entry point:	set operating context, print welcome message
24 *****************************************************************************
25 */
26	.section ".prefix", "ax", @progbits
27	jmp	$0x7c0, $1f
281:
29	/* Preserve registers for possible return to PXE */
30	pushfl
31	pushal
32	pushw	%gs
33	pushw	%fs
34	pushw	%es
35	pushw	%ds
36
37	/* Store magic word on PXE stack and remember PXE %ss:esp */
38	pushl	$STACK_MAGIC
39	movw	%ss, %cs:pxe_ss
40	movl	%esp, %cs:pxe_esp
41
42	/* Set up segments */
43	movw	%cs, %ax
44	movw	%ax, %ds
45	movw	$0x40, %ax		/* BIOS data segment access */
46	movw	%ax, %fs
47	/* Set up stack just below 0x7c00 */
48	xorw	%ax, %ax
49	movw	%ax, %ss
50	movl	$0x7c00, %esp
51	/* Clear direction flag, for the sake of sanity */
52	cld
53	/* Print welcome message */
54	movw	$10f, %si
55	xorw	%di, %di
56	call	print_message
57	.section ".prefix.data", "aw", @progbits
5810:	.asciz	"PXE->EB:"
59	.previous
60
61/*****************************************************************************
62 * Find us a usable !PXE or PXENV+ entry point
63 *****************************************************************************
64 */
65detect_pxe:
66	/* Plan A: !PXE pointer from the stack */
67	lgsl	pxe_esp, %ebp		/* %gs:%bp -> original stack */
68	lesw	%gs:52(%bp), %bx
69	call	is_valid_ppxe
70	je	have_ppxe
71
72	/* Plan B: PXENV+ pointer from initial ES:BX */
73	movw	%gs:32(%bp),%bx
74	movw	%gs:8(%bp),%es
75	call	is_valid_pxenv
76	je	have_pxenv
77
78	/* Plan C: PXENV+ structure via INT 1Ah */
79	movw	$0x5650, %ax
80	int	$0x1a
81	jc	1f
82	cmpw	$0x564e, %ax
83	jne	1f
84	call	is_valid_pxenv
85	je	have_pxenv
861:
87	/* Plan D: scan base memory for !PXE */
88	call	memory_scan_ppxe
89	je	have_ppxe
90
91	/* Plan E: scan base memory for PXENV+ */
92	call	memory_scan_pxenv
93	jne	stack_not_found
94
95have_pxenv:
96	movw	%bx, pxenv_offset
97	movw	%es, pxenv_segment
98
99	cmpw	$0x201, %es:6(%bx)	/* API version >= 2.01 */
100	jb	1f
101	cmpb	$0x2c, %es:8(%bx)	/* ... and structure long enough */
102	jb	2f
103
104	lesw	%es:0x28(%bx), %bx	/* Find !PXE from PXENV+ */
105	call	is_valid_ppxe
106	je	have_ppxe
1072:
108	call	memory_scan_ppxe	/* We are *supposed* to have !PXE... */
109	je	have_ppxe
1101:
111	lesw	pxenv_segoff, %bx	/* Nope, we're stuck with PXENV+ */
112
113	/* Record entry point and UNDI segments */
114	pushl	%es:0x0a(%bx)		/* Entry point */
115	pushw	%es:0x24(%bx)		/* UNDI code segment */
116	pushw	%es:0x26(%bx)		/* UNDI code size */
117	pushw	%es:0x20(%bx)		/* UNDI data segment */
118	pushw	%es:0x22(%bx)		/* UNDI data size */
119
120	/* Print "PXENV+ at <address>" */
121	movw	$10f, %si
122	jmp	check_have_stack
123	.section ".prefix.data", "aw", @progbits
12410:	.asciz	" PXENV+ at "
125	.previous
126
127have_ppxe:
128	movw	%bx, ppxe_offset
129	movw	%es, ppxe_segment
130
131	pushl	%es:0x10(%bx)		/* Entry point */
132	pushw	%es:0x30(%bx)		/* UNDI code segment */
133	pushw	%es:0x36(%bx)		/* UNDI code size */
134	pushw	%es:0x28(%bx)		/* UNDI data segment */
135	pushw	%es:0x2e(%bx)		/* UNDI data size */
136
137	/* Print "!PXE at <address>" */
138	movw	$10f, %si
139	jmp	check_have_stack
140	.section ".prefix.data", "aw", @progbits
14110:	.asciz	" !PXE at "
142	.previous
143
144is_valid_ppxe:
145	cmpl	$0x45585021, %es:(%bx)
146	jne	1f
147	movzbw	%es:4(%bx), %cx
148	cmpw	$0x58, %cx
149	jae	is_valid_checksum
1501:
151	ret
152
153is_valid_pxenv:
154	cmpl	$0x4e455850, %es:(%bx)
155	jne	1b
156	cmpw	$0x2b56, %es:4(%bx)
157	jne	1b
158	movzbw	%es:8(%bx), %cx
159	cmpw	$0x28, %cx
160	jb	1b
161
162is_valid_checksum:
163	pushw	%ax
164	movw	%bx, %si
165	xorw	%ax, %ax
1662:
167	es lodsb
168	addb	%al, %ah
169	loopw	2b
170	popw	%ax
171	ret
172
173memory_scan_ppxe:
174	movw	$is_valid_ppxe, %dx
175	jmp	memory_scan_common
176
177memory_scan_pxenv:
178	movw	$is_valid_pxenv, %dx
179
180memory_scan_common:
181	movw	%fs:(0x13), %ax
182	shlw	$6, %ax
183	decw	%ax
1841:	incw	%ax
185	cmpw	$( 0xa000 - 1 ), %ax
186	ja	2f
187	movw	%ax, %es
188	xorw	%bx, %bx
189	call	*%dx
190	jne	1b
1912:	ret
192
193/*****************************************************************************
194 * Sanity check: we must have an entry point
195 *****************************************************************************
196 */
197check_have_stack:
198	/* Save common values pushed onto the stack */
199	popl	undi_data_segoff
200	popl	undi_code_segoff
201	popl	entry_segoff
202
203	/* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
204	call	print_message
205	call	print_segoff
206	movb	$( ',' ), %al
207	call	print_character
208
209	/* Check for entry point */
210	movl	entry_segoff, %eax
211	testl	%eax, %eax
212	jnz	99f
213	/* No entry point: print message and skip everything else */
214stack_not_found:
215	movw	$10f, %si
216	call	print_message
217	jmp	finished
218	.section ".prefix.data", "aw", @progbits
21910:	.asciz	" No PXE stack found!\n"
220	.previous
22199:
222
223/*****************************************************************************
224 * Calculate base memory usage by UNDI
225 *****************************************************************************
226 */
227find_undi_basemem_usage:
228	movw	undi_code_segment, %ax
229	movw	undi_code_size, %bx
230	movw	undi_data_segment, %cx
231	movw	undi_data_size, %dx
232	cmpw	%ax, %cx
233	ja	1f
234	xchgw	%ax, %cx
235	xchgw	%bx, %dx
2361:	/* %ax:%bx now describes the lower region, %cx:%dx the higher */
237	shrw	$6, %ax			/* Round down to nearest kB */
238	movw	%ax, undi_fbms_start
239	addw	$0x0f, %dx		/* Round up to next segment */
240	shrw	$4, %dx
241	addw	%dx, %cx
242	addw	$((1024 / 16) - 1), %cx	/* Round up to next kB */
243	shrw	$6, %cx
244	movw	%cx, undi_fbms_end
245
246/*****************************************************************************
247 * Print information about detected PXE stack
248 *****************************************************************************
249 */
250print_structure_information:
251	/* Print entry point */
252	movw	$10f, %si
253	call	print_message
254	les	entry_segoff, %bx
255	call	print_segoff
256	.section ".prefix.data", "aw", @progbits
25710:	.asciz	" entry point at "
258	.previous
259	/* Print UNDI code segment */
260	movw	$10f, %si
261	call	print_message
262	les	undi_code_segoff, %bx
263	call	print_segoff
264	.section ".prefix.data", "aw", @progbits
26510:	.asciz	"\n         UNDI code segment "
266	.previous
267	/* Print UNDI data segment */
268	movw	$10f, %si
269	call	print_message
270	les	undi_data_segoff, %bx
271	call	print_segoff
272	.section ".prefix.data", "aw", @progbits
27310:	.asciz	", data segment "
274	.previous
275	/* Print UNDI memory usage */
276	movw	$10f, %si
277	call	print_message
278	movw	undi_fbms_start, %ax
279	call	print_word
280	movb	$( '-' ), %al
281	call	print_character
282	movw	undi_fbms_end, %ax
283	call	print_word
284	movw	$20f, %si
285	call	print_message
286	.section ".prefix.data", "aw", @progbits
28710:	.asciz	" ("
28820:	.asciz	"kB)\n"
289	.previous
290
291/*****************************************************************************
292 * Determine physical device
293 *****************************************************************************
294 */
295get_physical_device:
296	/* Issue PXENV_UNDI_GET_NIC_TYPE */
297	movw	$PXENV_UNDI_GET_NIC_TYPE, %bx
298	call	pxe_call
299	jnc	1f
300	call	print_pxe_error
301	jmp	no_physical_device
3021:	/* Determine physical device type */
303	movb	( pxe_parameter_structure + 0x02 ), %al
304	cmpb	$2, %al
305	je	pci_physical_device
306	jmp	no_physical_device
307
308pci_physical_device:
309	/* Record PCI bus:dev.fn and vendor/device IDs */
310	movl	( pxe_parameter_structure + 0x03 ), %eax
311	movl	%eax, pci_vendor
312	movw	( pxe_parameter_structure + 0x0b ), %ax
313	movw	%ax, pci_busdevfn
314	movw	$10f, %si
315	call	print_message
316	call	print_pci_busdevfn
317	jmp	99f
318	.section ".prefix.data", "aw", @progbits
31910:	.asciz	"         UNDI device is PCI "
320	.previous
321
322no_physical_device:
323	/* No device found, or device type not understood */
324	movw	$10f, %si
325	call	print_message
326	.section ".prefix.data", "aw", @progbits
32710:	.asciz	"         Unable to determine UNDI physical device"
328	.previous
329
33099:
331
332/*****************************************************************************
333 * Determine interface type
334 *****************************************************************************
335 */
336get_iface_type:
337	/* Issue PXENV_UNDI_GET_IFACE_INFO */
338	movw	$PXENV_UNDI_GET_IFACE_INFO, %bx
339	call	pxe_call
340	jnc	1f
341	call	print_pxe_error
342	jmp	99f
3431:	/* Print interface type */
344	movw	$10f, %si
345	call	print_message
346	leaw	( pxe_parameter_structure + 0x02 ), %si
347	call	print_message
348	.section ".prefix.data", "aw", @progbits
34910:	.asciz	", type "
350	.previous
351	/* Check for "Etherboot" interface type */
352	cmpl	$EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
353	jne	99f
354	cmpl	$EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
355	jne	99f
356	movw	$10f, %si
357	call	print_message
358	.section ".prefix.data", "aw", @progbits
35910:	.asciz	" (workaround enabled)"
360	.previous
361	/* Flag Etherboot workarounds as required */
362	orw	$PXE_HACK_EB54, pxe_hacks
363
36499:	movb	$0x0a, %al
365	call	print_character
366
367/*****************************************************************************
368 * Leave NIC in a safe state
369 *****************************************************************************
370 */
371#ifndef PXELOADER_KEEP_PXE
372shutdown_nic:
373	/* Issue PXENV_UNDI_SHUTDOWN */
374	movw	$PXENV_UNDI_SHUTDOWN, %bx
375	call	pxe_call
376	jnc	1f
377	call	print_pxe_error
3781:
379unload_base_code:
380	/* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
381	 * we must not issue this call if the underlying stack is
382	 * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
383	 */
384#ifdef PXELOADER_KEEP_UNDI
385	testw	$PXE_HACK_EB54, pxe_hacks
386	jnz	99f
387#endif /* PXELOADER_KEEP_UNDI */
388	/* Issue PXENV_UNLOAD_STACK */
389	movw	$PXENV_UNLOAD_STACK, %bx
390	call	pxe_call
391	jnc	1f
392	call	print_pxe_error
393	jmp	99f
3941:	/* Free base memory used by PXE base code */
395	movw	undi_fbms_start, %ax
396	movw	%fs:(0x13), %bx
397	call	free_basemem
39899:
399	andw	$~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
400#endif /* PXELOADER_KEEP_PXE */
401
402/*****************************************************************************
403 * Unload UNDI driver
404 *****************************************************************************
405 */
406#ifndef PXELOADER_KEEP_UNDI
407unload_undi:
408	/* Issue PXENV_STOP_UNDI */
409	movw	$PXENV_STOP_UNDI, %bx
410	call	pxe_call
411	jnc	1f
412	call	print_pxe_error
413	jmp	99f
4141:	/* Free base memory used by UNDI */
415	movw	undi_fbms_end, %ax
416	movw	undi_fbms_start, %bx
417	call	free_basemem
418	/* Clear UNDI_FL_STARTED */
419	andw	$~UNDI_FL_STARTED, flags
42099:
421#endif /* PXELOADER_KEEP_UNDI */
422
423/*****************************************************************************
424 * Print remaining free base memory
425 *****************************************************************************
426 */
427print_free_basemem:
428	movw	$10f, %si
429	call	print_message
430	movw	%fs:(0x13), %ax
431	call	print_word
432	movw	$20f, %si
433	call	print_message
434	.section ".prefix.data", "aw", @progbits
43510:	.asciz	"         "
43620:	.asciz	"kB free base memory after PXE unload\n"
437	.previous
438
439/*****************************************************************************
440 * Exit point
441 *****************************************************************************
442 */
443finished:
444	jmp	run_gpxe
445
446/*****************************************************************************
447 * Subroutine: print segment:offset address
448 *
449 * Parameters:
450 *   %es:%bx : segment:offset address to print
451 *   %ds:di : output buffer (or %di=0 to print to console)
452 * Returns:
453 *   %ds:di : next character in output buffer (if applicable)
454 *****************************************************************************
455 */
456print_segoff:
457	/* Preserve registers */
458	pushw	%ax
459	/* Print "<segment>:offset" */
460	movw	%es, %ax
461	call	print_hex_word
462	movb	$( ':' ), %al
463	call	print_character
464	movw	%bx, %ax
465	call	print_hex_word
466	/* Restore registers and return */
467	popw	%ax
468	ret
469
470/*****************************************************************************
471 * Subroutine: print decimal word
472 *
473 * Parameters:
474 *   %ax : word to print
475 *   %ds:di : output buffer (or %di=0 to print to console)
476 * Returns:
477 *   %ds:di : next character in output buffer (if applicable)
478 *****************************************************************************
479 */
480print_word:
481	/* Preserve registers */
482	pushw	%ax
483	pushw	%bx
484	pushw	%cx
485	pushw	%dx
486	/* Build up digit sequence on stack */
487	movw	$10, %bx
488	xorw	%cx, %cx
4891:	xorw	%dx, %dx
490	divw	%bx, %ax
491	pushw	%dx
492	incw	%cx
493	testw	%ax, %ax
494	jnz	1b
495	/* Print digit sequence */
4961:	popw	%ax
497	call	print_hex_nibble
498	loop	1b
499	/* Restore registers and return */
500	popw	%dx
501	popw	%cx
502	popw	%bx
503	popw	%ax
504	ret
505
506/*****************************************************************************
507 * Subroutine: zero 1kB block of base memory
508 *
509 * Parameters:
510 *   %bx : block to zero (in kB)
511 * Returns:
512 *   Nothing
513 *****************************************************************************
514 */
515zero_kb:
516	/* Preserve registers */
517	pushw	%ax
518	pushw	%cx
519	pushw	%di
520	pushw	%es
521	/* Zero block */
522	movw	%bx, %ax
523	shlw	$6, %ax
524	movw	%ax, %es
525	movw	$0x400, %cx
526	xorw	%di, %di
527	xorw	%ax, %ax
528	rep stosb
529	/* Restore registers and return */
530	popw	%es
531	popw	%di
532	popw	%cx
533	popw	%ax
534	ret
535
536/*****************************************************************************
537 * Subroutine: free and zero base memory
538 *
539 * Parameters:
540 *   %ax : Desired new free base memory counter (in kB)
541 *   %bx : Expected current free base memory counter (in kB)
542 *   %fs : BIOS data segment (0x40)
543 * Returns:
544 *   None
545 *
546 * The base memory from %bx kB to %ax kB is unconditionally zeroed.
547 * It will be freed if and only if the expected current free base
548 * memory counter (%bx) matches the actual current free base memory
549 * counter in 0x40:0x13; if this does not match then the memory will
550 * be leaked.
551 *****************************************************************************
552 */
553free_basemem:
554	/* Zero base memory */
555	pushw	%bx
5561:	cmpw	%bx, %ax
557	je	2f
558	call	zero_kb
559	incw	%bx
560	jmp	1b
5612:	popw	%bx
562	/* Free base memory */
563	cmpw	%fs:(0x13), %bx		/* Update FBMS only if "old" value  */
564	jne	1f			/* is correct			    */
5651:	movw	%ax, %fs:(0x13)
566	ret
567
568/*****************************************************************************
569 * Subroutine: make a PXE API call.  Works with either !PXE or PXENV+ API.
570 *
571 * Parameters:
572 *   %bx : PXE API call number
573 *   %ds:pxe_parameter_structure : Parameters for PXE API call
574 * Returns:
575 *   %ax : PXE status code (not exit code)
576 *   CF set if %ax is non-zero
577 *****************************************************************************
578 */
579pxe_call:
580	/* Preserve registers */
581	pushw	%di
582	pushw	%es
583	/* Set up registers for PXENV+ API.  %bx already set up */
584	pushw	%ds
585	popw	%es
586	movw	$pxe_parameter_structure, %di
587	/* Set up stack for !PXE API */
588	pushw   %es
589	pushw	%di
590	pushw	%bx
591	/* Make the API call */
592	lcall	*entry_segoff
593	/* Reset the stack */
594	addw	$6, %sp
595	movw	pxe_parameter_structure, %ax
596	clc
597	testw	%ax, %ax
598	jz	1f
599	stc
6001:	/* Clear direction flag, for the sake of sanity */
601	cld
602	/* Restore registers and return */
603	popw	%es
604	popw	%di
605	ret
606
607/*****************************************************************************
608 * Subroutine: print PXE API call error message
609 *
610 * Parameters:
611 *   %ax : PXE status code
612 *   %bx : PXE API call number
613 * Returns:
614 *   Nothing
615 *****************************************************************************
616 */
617print_pxe_error:
618	pushw	%si
619	movw	$10f, %si
620	call	print_message
621	xchgw	%ax, %bx
622	call	print_hex_word
623	movw	$20f, %si
624	call	print_message
625	xchgw	%ax, %bx
626	call	print_hex_word
627	movw	$30f, %si
628	call	print_message
629	popw	%si
630	ret
631	.section ".prefix.data", "aw", @progbits
63210:	.asciz	"         UNDI API call "
63320:	.asciz	" failed: status code "
63430:	.asciz	"\n"
635	.previous
636
637/*****************************************************************************
638 * PXE data structures
639 *****************************************************************************
640 */
641	.section ".prefix.data"
642
643pxe_esp:		.long 0
644pxe_ss:			.word 0
645
646pxe_parameter_structure: .fill 64
647
648undi_code_segoff:
649undi_code_size:		.word 0
650undi_code_segment:	.word 0
651
652undi_data_segoff:
653undi_data_size:		.word 0
654undi_data_segment:	.word 0
655
656pxe_hacks:		.word 0
657
658/* The following fields are part of a struct undi_device */
659
660undi_device:
661
662pxenv_segoff:
663pxenv_offset:		.word 0
664pxenv_segment:		.word 0
665
666ppxe_segoff:
667ppxe_offset:		.word 0
668ppxe_segment:		.word 0
669
670entry_segoff:
671entry_offset:		.word 0
672entry_segment:		.word 0
673
674undi_fbms_start:	.word 0
675undi_fbms_end:		.word 0
676
677pci_busdevfn:		.word UNDI_NO_PCI_BUSDEVFN
678isapnp_csn:		.word UNDI_NO_ISAPNP_CSN
679isapnp_read_port:	.word UNDI_NO_ISAPNP_READ_PORT
680
681pci_vendor:		.word 0
682pci_device:		.word 0
683flags:
684	.word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
685
686	.equ undi_device_size, ( . - undi_device )
687
688/*****************************************************************************
689 * Run gPXE main code
690 *****************************************************************************
691 */
692	.section ".prefix"
693run_gpxe:
694	/* Install gPXE */
695	call	install
696
697	/* Set up real-mode stack */
698	movw	%bx, %ss
699	movw	$_estack16, %sp
700
701#ifdef PXELOADER_KEEP_UNDI
702	/* Copy our undi_device structure to the preloaded_undi variable */
703	movw	%bx, %es
704	movw	$preloaded_undi, %di
705	movw	$undi_device, %si
706	movw	$undi_device_size, %cx
707	rep movsb
708#endif
709
710	/* Retrieve PXE %ss:esp */
711	movw	pxe_ss,	%di
712	movl	pxe_esp, %ebp
713
714	/* Jump to .text16 segment with %ds pointing to .data16 */
715	movw	%bx, %ds
716	pushw	%ax
717	pushw	$1f
718	lret
719	.section ".text16", "ax", @progbits
7201:
721	/* Update the exit hook */
722	movw	%cs,pxe_exit_hook+2
723	push	%ax
724	mov	$2f,%ax
725	mov	%ax,pxe_exit_hook
726	pop	%ax
727
728	/* Run main program */
729	pushl	$main
730	pushw	%cs
731	call	prot_call
732	popl	%ecx /* discard */
733
734	/* Uninstall gPXE */
735	call	uninstall
736
737	/* Restore PXE stack */
738	movw	%di, %ss
739	movl	%ebp, %esp
740
741	/* Jump to hook if applicable */
742	ljmpw	*pxe_exit_hook
743
7442:	/* Check PXE stack magic */
745	popl	%eax
746	cmpl	$STACK_MAGIC, %eax
747	jne	1f
748
749	/* PXE stack OK: return to caller */
750	popw	%ds
751	popw	%es
752	popw	%fs
753	popw	%gs
754	popal
755	popfl
756	xorw	%ax, %ax	/* Return success */
757	lret
758
7591:	/* PXE stack corrupt or removed: use INT 18 */
760	int	$0x18
761	.previous
762