xref: /titanic_51/usr/src/grub/grub-0.97/netboot/undi.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1*1b8adde7SWilliam Kucharski /**************************************************************************
2*1b8adde7SWilliam Kucharski Etherboot -  BOOTP/TFTP Bootstrap Program
3*1b8adde7SWilliam Kucharski UNDI NIC driver for Etherboot
4*1b8adde7SWilliam Kucharski 
5*1b8adde7SWilliam Kucharski This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
6*1b8adde7SWilliam Kucharski of Fen Systems Ltd. (http://www.fensystems.co.uk/).  All rights
7*1b8adde7SWilliam Kucharski reserved.
8*1b8adde7SWilliam Kucharski 
9*1b8adde7SWilliam Kucharski $Id: undi.c,v 1.8 2003/10/25 13:54:53 mcb30 Exp $
10*1b8adde7SWilliam Kucharski ***************************************************************************/
11*1b8adde7SWilliam Kucharski 
12*1b8adde7SWilliam Kucharski /*
13*1b8adde7SWilliam Kucharski  * This program is free software; you can redistribute it and/or
14*1b8adde7SWilliam Kucharski  * modify it under the terms of the GNU General Public License as
15*1b8adde7SWilliam Kucharski  * published by the Free Software Foundation; either version 2, or (at
16*1b8adde7SWilliam Kucharski  * your option) any later version.
17*1b8adde7SWilliam Kucharski  */
18*1b8adde7SWilliam Kucharski 
19*1b8adde7SWilliam Kucharski /* to get some global routines like printf */
20*1b8adde7SWilliam Kucharski #include "etherboot.h"
21*1b8adde7SWilliam Kucharski /* to get the interface to the body of the program */
22*1b8adde7SWilliam Kucharski #include "nic.h"
23*1b8adde7SWilliam Kucharski /* to get the PCI support functions, if this is a PCI NIC */
24*1b8adde7SWilliam Kucharski #include "pci.h"
25*1b8adde7SWilliam Kucharski /* UNDI and PXE defines.  Includes pxe.h. */
26*1b8adde7SWilliam Kucharski #include "undi.h"
27*1b8adde7SWilliam Kucharski /* 8259 PIC defines */
28*1b8adde7SWilliam Kucharski #include "pic8259.h"
29*1b8adde7SWilliam Kucharski #include "bootp.h"
30*1b8adde7SWilliam Kucharski #include "tftp.h"
31*1b8adde7SWilliam Kucharski #include "shared.h"
32*1b8adde7SWilliam Kucharski 
33*1b8adde7SWilliam Kucharski /* NIC specific static variables go here */
34*1b8adde7SWilliam Kucharski static undi_t undi = { NULL, NULL, NULL, NULL, NULL, NULL, NULL,
35*1b8adde7SWilliam Kucharski 		       NULL, NULL, 0, NULL, 0, NULL,
36*1b8adde7SWilliam Kucharski 		       0, 0, 0, 0,
37*1b8adde7SWilliam Kucharski 		       { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL },
38*1b8adde7SWilliam Kucharski 		       IRQ_NONE };
39*1b8adde7SWilliam Kucharski static undi_base_mem_data_t undi_base_mem_data;
40*1b8adde7SWilliam Kucharski 
41*1b8adde7SWilliam Kucharski #define UNDI_HEAP (void *)(512 << 10)
42*1b8adde7SWilliam Kucharski 
43*1b8adde7SWilliam Kucharski /* Function prototypes */
44*1b8adde7SWilliam Kucharski int allocate_base_mem_data ( void );
45*1b8adde7SWilliam Kucharski int free_base_mem_data ( void );
46*1b8adde7SWilliam Kucharski int eb_pxenv_undi_shutdown ( void );
47*1b8adde7SWilliam Kucharski int eb_pxenv_stop_undi ( void );
48*1b8adde7SWilliam Kucharski int undi_unload_base_code ( void );
49*1b8adde7SWilliam Kucharski int undi_full_shutdown ( void );
50*1b8adde7SWilliam Kucharski int eb_pxenv_get_cached_info (uint8_t, void **info);
51*1b8adde7SWilliam Kucharski 
52*1b8adde7SWilliam Kucharski /**************************************************************************
53*1b8adde7SWilliam Kucharski  * Utility functions
54*1b8adde7SWilliam Kucharski  **************************************************************************/
55*1b8adde7SWilliam Kucharski 
56*1b8adde7SWilliam Kucharski /* Checksum a block.
57*1b8adde7SWilliam Kucharski  */
58*1b8adde7SWilliam Kucharski 
59*1b8adde7SWilliam Kucharski uint8_t checksum ( void *block, size_t size ) {
60*1b8adde7SWilliam Kucharski 	uint8_t sum = 0;
61*1b8adde7SWilliam Kucharski 	uint16_t i = 0;
62*1b8adde7SWilliam Kucharski 	for ( i = 0; i < size; i++ ) {
63*1b8adde7SWilliam Kucharski 		sum += ( ( uint8_t * ) block )[i];
64*1b8adde7SWilliam Kucharski 	}
65*1b8adde7SWilliam Kucharski 	return sum;
66*1b8adde7SWilliam Kucharski }
67*1b8adde7SWilliam Kucharski 
68*1b8adde7SWilliam Kucharski /* Print the status of a !PXE structure
69*1b8adde7SWilliam Kucharski  */
70*1b8adde7SWilliam Kucharski 
71*1b8adde7SWilliam Kucharski void pxe_dump ( void ) {
72*1b8adde7SWilliam Kucharski #ifdef TRACE_UNDI
73*1b8adde7SWilliam Kucharski 	printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
74*1b8adde7SWilliam Kucharski 		 "BD %hx:%hx BC %hx:%hx\n",
75*1b8adde7SWilliam Kucharski 		 undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset,
76*1b8adde7SWilliam Kucharski 		 undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size,
77*1b8adde7SWilliam Kucharski 		 undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size,
78*1b8adde7SWilliam Kucharski 		 undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size,
79*1b8adde7SWilliam Kucharski 		 undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size,
80*1b8adde7SWilliam Kucharski 		 undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size );
81*1b8adde7SWilliam Kucharski #endif
82*1b8adde7SWilliam Kucharski }
83*1b8adde7SWilliam Kucharski 
84*1b8adde7SWilliam Kucharski /* Allocate/free space for structures that must reside in base memory
85*1b8adde7SWilliam Kucharski  */
86*1b8adde7SWilliam Kucharski 
87*1b8adde7SWilliam Kucharski int allocate_base_mem_data ( void ) {
88*1b8adde7SWilliam Kucharski 	/* In GRUB, anything is in base address, so we do not need
89*1b8adde7SWilliam Kucharski 	 * allocate anything */
90*1b8adde7SWilliam Kucharski 	undi.base_mem_data = &undi_base_mem_data;
91*1b8adde7SWilliam Kucharski 	memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
92*1b8adde7SWilliam Kucharski 	undi.undi_call_info = &undi.base_mem_data->undi_call_info;
93*1b8adde7SWilliam Kucharski 	undi.pxs = &undi.base_mem_data->pxs;
94*1b8adde7SWilliam Kucharski 	undi.xmit_data = &undi.base_mem_data->xmit_data;
95*1b8adde7SWilliam Kucharski 	undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
96*1b8adde7SWilliam Kucharski #if 0				/* Etherboot Code */
97*1b8adde7SWilliam Kucharski 	/* Allocate space in base memory.
98*1b8adde7SWilliam Kucharski 	 * Initialise pointers to base memory structures.
99*1b8adde7SWilliam Kucharski 	 */
100*1b8adde7SWilliam Kucharski 	if ( undi.base_mem_data == NULL ) {
101*1b8adde7SWilliam Kucharski 		undi.base_mem_data =
102*1b8adde7SWilliam Kucharski 			allot_base_memory ( sizeof(undi_base_mem_data_t) +
103*1b8adde7SWilliam Kucharski 					    TRIVIAL_IRQ_HANDLER_SIZE );
104*1b8adde7SWilliam Kucharski 		if ( undi.base_mem_data == NULL ) {
105*1b8adde7SWilliam Kucharski 			printf ( "Failed to allocate base memory\n" );
106*1b8adde7SWilliam Kucharski 			free_base_mem_data();
107*1b8adde7SWilliam Kucharski 			return 0;
108*1b8adde7SWilliam Kucharski 		}
109*1b8adde7SWilliam Kucharski 		memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
110*1b8adde7SWilliam Kucharski 		undi.undi_call_info = &undi.base_mem_data->undi_call_info;
111*1b8adde7SWilliam Kucharski 		undi.pxs = &undi.base_mem_data->pxs;
112*1b8adde7SWilliam Kucharski 		undi.xmit_data = &undi.base_mem_data->xmit_data;
113*1b8adde7SWilliam Kucharski 		undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
114*1b8adde7SWilliam Kucharski 		copy_trivial_irq_handler ( undi.base_mem_data->irq_handler,
115*1b8adde7SWilliam Kucharski 					   TRIVIAL_IRQ_HANDLER_SIZE );
116*1b8adde7SWilliam Kucharski 	}
117*1b8adde7SWilliam Kucharski #endif	/* Etherboot Code */
118*1b8adde7SWilliam Kucharski 	return 1;
119*1b8adde7SWilliam Kucharski }
120*1b8adde7SWilliam Kucharski 
121*1b8adde7SWilliam Kucharski int free_base_mem_data ( void ) {
122*1b8adde7SWilliam Kucharski 	/* Just pretend to free something :-) */
123*1b8adde7SWilliam Kucharski 	undi.base_mem_data = NULL;
124*1b8adde7SWilliam Kucharski 	undi.undi_call_info = NULL;
125*1b8adde7SWilliam Kucharski 	undi.pxs = NULL;
126*1b8adde7SWilliam Kucharski 	undi.xmit_data = NULL;
127*1b8adde7SWilliam Kucharski 	undi.xmit_buffer = NULL;
128*1b8adde7SWilliam Kucharski #if 0				/* Etherboot Code */
129*1b8adde7SWilliam Kucharski 	if ( undi.base_mem_data != NULL ) {
130*1b8adde7SWilliam Kucharski 		forget_base_memory ( undi.base_mem_data,
131*1b8adde7SWilliam Kucharski 				     sizeof(undi_base_mem_data_t) +
132*1b8adde7SWilliam Kucharski 				     TRIVIAL_IRQ_HANDLER_SIZE );
133*1b8adde7SWilliam Kucharski 		undi.base_mem_data = NULL;
134*1b8adde7SWilliam Kucharski 		undi.undi_call_info = NULL;
135*1b8adde7SWilliam Kucharski 		undi.pxs = NULL;
136*1b8adde7SWilliam Kucharski 		undi.xmit_data = NULL;
137*1b8adde7SWilliam Kucharski 		undi.xmit_buffer = NULL;
138*1b8adde7SWilliam Kucharski 		copy_trivial_irq_handler ( NULL, 0 );
139*1b8adde7SWilliam Kucharski 	}
140*1b8adde7SWilliam Kucharski #endif	/* Etherboot Code */
141*1b8adde7SWilliam Kucharski 	return 1;
142*1b8adde7SWilliam Kucharski }
143*1b8adde7SWilliam Kucharski 
144*1b8adde7SWilliam Kucharski void assemble_firing_squad ( firing_squad_lineup_t *lineup,
145*1b8adde7SWilliam Kucharski 			     void *start, size_t size,
146*1b8adde7SWilliam Kucharski 			     firing_squad_shoot_t shoot ) {
147*1b8adde7SWilliam Kucharski 	int target;
148*1b8adde7SWilliam Kucharski 	int index;
149*1b8adde7SWilliam Kucharski 	int bit;
150*1b8adde7SWilliam Kucharski 	int start_kb = virt_to_phys(start) >> 10;
151*1b8adde7SWilliam Kucharski 	int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
152*1b8adde7SWilliam Kucharski 
153*1b8adde7SWilliam Kucharski 	for ( target = start_kb; target <= end_kb; target++ ) {
154*1b8adde7SWilliam Kucharski 		index = FIRING_SQUAD_TARGET_INDEX ( target );
155*1b8adde7SWilliam Kucharski 		bit = FIRING_SQUAD_TARGET_BIT ( target );
156*1b8adde7SWilliam Kucharski 		lineup->targets[index] = ( shoot << bit ) |
157*1b8adde7SWilliam Kucharski 			( lineup->targets[index] & ~( 1 << bit ) );
158*1b8adde7SWilliam Kucharski 	}
159*1b8adde7SWilliam Kucharski }
160*1b8adde7SWilliam Kucharski 
161*1b8adde7SWilliam Kucharski void shoot_targets ( firing_squad_lineup_t *lineup ) {
162*1b8adde7SWilliam Kucharski 	int shoot_this_target = 0;
163*1b8adde7SWilliam Kucharski 	int shoot_last_target = 0;
164*1b8adde7SWilliam Kucharski 	int start_target = 0;
165*1b8adde7SWilliam Kucharski 	int target;
166*1b8adde7SWilliam Kucharski 
167*1b8adde7SWilliam Kucharski 	for ( target = 0; target <= 640; target++ ) {
168*1b8adde7SWilliam Kucharski 		shoot_this_target = ( target == 640 ? 0 :
169*1b8adde7SWilliam Kucharski 		      ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) &
170*1b8adde7SWilliam Kucharski 		      lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] );
171*1b8adde7SWilliam Kucharski 		if ( shoot_this_target && !shoot_last_target ) {
172*1b8adde7SWilliam Kucharski 			start_target = target;
173*1b8adde7SWilliam Kucharski 		} else if ( shoot_last_target && !shoot_this_target ) {
174*1b8adde7SWilliam Kucharski 			size_t range_size = ( target - start_target ) << 10;
175*1b8adde7SWilliam Kucharski 			forget_base_memory ( phys_to_virt( start_target<<10 ),
176*1b8adde7SWilliam Kucharski 					     range_size );
177*1b8adde7SWilliam Kucharski 		}
178*1b8adde7SWilliam Kucharski 		shoot_last_target = shoot_this_target;
179*1b8adde7SWilliam Kucharski 	}
180*1b8adde7SWilliam Kucharski }
181*1b8adde7SWilliam Kucharski 
182*1b8adde7SWilliam Kucharski /* Debug macros
183*1b8adde7SWilliam Kucharski  */
184*1b8adde7SWilliam Kucharski 
185*1b8adde7SWilliam Kucharski #ifdef TRACE_UNDI
186*1b8adde7SWilliam Kucharski #define DBG(...) printf ( __VA_ARGS__ )
187*1b8adde7SWilliam Kucharski #else
188*1b8adde7SWilliam Kucharski #define DBG(...)
189*1b8adde7SWilliam Kucharski #endif
190*1b8adde7SWilliam Kucharski 
191*1b8adde7SWilliam Kucharski #define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
192*1b8adde7SWilliam Kucharski 			      "SUCCESS" : \
193*1b8adde7SWilliam Kucharski 			      ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
194*1b8adde7SWilliam Kucharski 				"FAILURE" : "UNKNOWN" ) )
195*1b8adde7SWilliam Kucharski 
196*1b8adde7SWilliam Kucharski /**************************************************************************
197*1b8adde7SWilliam Kucharski  * Base memory scanning functions
198*1b8adde7SWilliam Kucharski  **************************************************************************/
199*1b8adde7SWilliam Kucharski 
200*1b8adde7SWilliam Kucharski /* Locate the $PnP structure indicating a PnP BIOS.
201*1b8adde7SWilliam Kucharski  */
202*1b8adde7SWilliam Kucharski 
203*1b8adde7SWilliam Kucharski int hunt_pnp_bios ( void ) {
204*1b8adde7SWilliam Kucharski 	uint32_t off = 0x10000;
205*1b8adde7SWilliam Kucharski 
206*1b8adde7SWilliam Kucharski 	DBG ( "Hunting for PnP BIOS..." );
207*1b8adde7SWilliam Kucharski 	while ( off > 0 ) {
208*1b8adde7SWilliam Kucharski 		off -= 16;
209*1b8adde7SWilliam Kucharski 		undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off );
210*1b8adde7SWilliam Kucharski 		if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) {
211*1b8adde7SWilliam Kucharski 			DBG ( "found $PnP at f000:%hx...", off );
212*1b8adde7SWilliam Kucharski 			if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) {
213*1b8adde7SWilliam Kucharski 				DBG ( "invalid checksum\n..." );
214*1b8adde7SWilliam Kucharski 				continue;
215*1b8adde7SWilliam Kucharski 			}
216*1b8adde7SWilliam Kucharski 			DBG ( "ok\n" );
217*1b8adde7SWilliam Kucharski 			return 1;
218*1b8adde7SWilliam Kucharski 		}
219*1b8adde7SWilliam Kucharski 	}
220*1b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
221*1b8adde7SWilliam Kucharski 	undi.pnp_bios = NULL;
222*1b8adde7SWilliam Kucharski 	return 0;
223*1b8adde7SWilliam Kucharski }
224*1b8adde7SWilliam Kucharski 
225*1b8adde7SWilliam Kucharski /* Locate the !PXE structure indicating a loaded UNDI driver.
226*1b8adde7SWilliam Kucharski  */
227*1b8adde7SWilliam Kucharski 
228*1b8adde7SWilliam Kucharski int hunt_pixie ( void ) {
229*1b8adde7SWilliam Kucharski 	static uint32_t ptr = 0;
230*1b8adde7SWilliam Kucharski 	pxe_t *pxe = NULL;
231*1b8adde7SWilliam Kucharski 
232*1b8adde7SWilliam Kucharski 	DBG ( "Hunting for pixies..." );
233*1b8adde7SWilliam Kucharski 	if ( ptr == 0 ) ptr = 0xa0000;
234*1b8adde7SWilliam Kucharski 	while ( ptr > 0x10000 ) {
235*1b8adde7SWilliam Kucharski 		ptr -= 16;
236*1b8adde7SWilliam Kucharski 		pxe = (pxe_t *) phys_to_virt ( ptr );
237*1b8adde7SWilliam Kucharski 		if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) {
238*1b8adde7SWilliam Kucharski 			DBG ( "found !PXE at %x...", ptr );
239*1b8adde7SWilliam Kucharski 			if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
240*1b8adde7SWilliam Kucharski 				DBG ( "invalid checksum\n..." );
241*1b8adde7SWilliam Kucharski 				continue;
242*1b8adde7SWilliam Kucharski 			}
243*1b8adde7SWilliam Kucharski 			if ( ptr < get_free_base_memory() ) {
244*1b8adde7SWilliam Kucharski 				DBG ( "in free base memory!\n\n"
245*1b8adde7SWilliam Kucharski 					 "WARNING: a valid !PXE structure was "
246*1b8adde7SWilliam Kucharski 					 "found in an area of memory marked "
247*1b8adde7SWilliam Kucharski 					 "as free!\n\n" );
248*1b8adde7SWilliam Kucharski 				undi.pxe = pxe;
249*1b8adde7SWilliam Kucharski 				pxe_dump();
250*1b8adde7SWilliam Kucharski 				undi.pxe = NULL;
251*1b8adde7SWilliam Kucharski 				DBG ( "\nIgnoring and continuing, but this "
252*1b8adde7SWilliam Kucharski 					 "may cause problems later!\n\n" );
253*1b8adde7SWilliam Kucharski 				continue;
254*1b8adde7SWilliam Kucharski 			}
255*1b8adde7SWilliam Kucharski 			DBG ( "ok\n" );
256*1b8adde7SWilliam Kucharski 			undi.pxe = pxe;
257*1b8adde7SWilliam Kucharski 			pxe_dump();
258*1b8adde7SWilliam Kucharski 			DBG ( "Resetting pixie...\n" );
259*1b8adde7SWilliam Kucharski 			undi_unload_base_code();
260*1b8adde7SWilliam Kucharski 			eb_pxenv_stop_undi();
261*1b8adde7SWilliam Kucharski 			pxe_dump();
262*1b8adde7SWilliam Kucharski 			return 1;
263*1b8adde7SWilliam Kucharski 		}
264*1b8adde7SWilliam Kucharski 	}
265*1b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
266*1b8adde7SWilliam Kucharski 	ptr = 0;
267*1b8adde7SWilliam Kucharski 	return 0;
268*1b8adde7SWilliam Kucharski }
269*1b8adde7SWilliam Kucharski 
270*1b8adde7SWilliam Kucharski /* Locate PCI PnP ROMs.
271*1b8adde7SWilliam Kucharski  */
272*1b8adde7SWilliam Kucharski 
273*1b8adde7SWilliam Kucharski int hunt_rom ( void ) {
274*1b8adde7SWilliam Kucharski 	static uint32_t ptr = 0;
275*1b8adde7SWilliam Kucharski 
276*1b8adde7SWilliam Kucharski 	DBG ( "Hunting for ROMs..." );
277*1b8adde7SWilliam Kucharski 	if ( ptr == 0 ) ptr = 0x100000;
278*1b8adde7SWilliam Kucharski 	while ( ptr > 0x0c0000 ) {
279*1b8adde7SWilliam Kucharski 		ptr -= 0x800;
280*1b8adde7SWilliam Kucharski 		undi.rom = ( rom_t * ) phys_to_virt ( ptr );
281*1b8adde7SWilliam Kucharski 		if ( undi.rom->signature == ROM_SIGNATURE ) {
282*1b8adde7SWilliam Kucharski 			pcir_header_t *pcir_header = NULL;
283*1b8adde7SWilliam Kucharski 			pnp_header_t *pnp_header = NULL;
284*1b8adde7SWilliam Kucharski 
285*1b8adde7SWilliam Kucharski 			DBG ( "found 55AA at %x...", ptr );
286*1b8adde7SWilliam Kucharski 			if ( undi.rom->pcir_off == 0 ) {
287*1b8adde7SWilliam Kucharski 				DBG ( "not a PCI ROM\n..." );
288*1b8adde7SWilliam Kucharski 				continue;
289*1b8adde7SWilliam Kucharski 			}
290*1b8adde7SWilliam Kucharski 			pcir_header = (pcir_header_t*)( ( void * ) undi.rom +
291*1b8adde7SWilliam Kucharski 							undi.rom->pcir_off );
292*1b8adde7SWilliam Kucharski 			if ( pcir_header->signature != PCIR_SIGNATURE ) {
293*1b8adde7SWilliam Kucharski 				DBG ( "invalid PCI signature\n..." );
294*1b8adde7SWilliam Kucharski 				continue;
295*1b8adde7SWilliam Kucharski 			}
296*1b8adde7SWilliam Kucharski 			DBG ( "PCI:%hx:%hx...", pcir_header->vendor_id,
297*1b8adde7SWilliam Kucharski 				 pcir_header->device_id );
298*1b8adde7SWilliam Kucharski 			if ( ( pcir_header->vendor_id != undi.pci.vendor ) ||
299*1b8adde7SWilliam Kucharski 			     ( pcir_header->device_id != undi.pci.dev_id ) ) {
300*1b8adde7SWilliam Kucharski 				DBG ( "not me (%hx:%hx)\n...",
301*1b8adde7SWilliam Kucharski 					 undi.pci.vendor,
302*1b8adde7SWilliam Kucharski 					 undi.pci.dev_id );
303*1b8adde7SWilliam Kucharski 				continue;
304*1b8adde7SWilliam Kucharski 			}
305*1b8adde7SWilliam Kucharski 			if ( undi.rom->pnp_off == 0 ) {
306*1b8adde7SWilliam Kucharski 				DBG ( "not a PnP ROM\n..." );
307*1b8adde7SWilliam Kucharski 				continue;
308*1b8adde7SWilliam Kucharski 			}
309*1b8adde7SWilliam Kucharski 			pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
310*1b8adde7SWilliam Kucharski 							 undi.rom->pnp_off );
311*1b8adde7SWilliam Kucharski 			if ( pnp_header->signature != PNP_SIGNATURE ) {
312*1b8adde7SWilliam Kucharski 				DBG ( "invalid $PnP signature\n..." );
313*1b8adde7SWilliam Kucharski 				continue;
314*1b8adde7SWilliam Kucharski 			}
315*1b8adde7SWilliam Kucharski 			if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
316*1b8adde7SWilliam Kucharski 				DBG ( "invalid PnP checksum\n..." );
317*1b8adde7SWilliam Kucharski 				continue;
318*1b8adde7SWilliam Kucharski 			}
319*1b8adde7SWilliam Kucharski 			DBG ( "ok\n");
320*1b8adde7SWilliam Kucharski 			printf ("ROM %s by %s\n",
321*1b8adde7SWilliam Kucharski 				 pnp_header->product_str_off==0 ? "(unknown)" :
322*1b8adde7SWilliam Kucharski 				 (void*)undi.rom+pnp_header->product_str_off,
323*1b8adde7SWilliam Kucharski 				 pnp_header->manuf_str_off==0 ? "(unknown)" :
324*1b8adde7SWilliam Kucharski 				 (void*)undi.rom+pnp_header->manuf_str_off );
325*1b8adde7SWilliam Kucharski 			return 1;
326*1b8adde7SWilliam Kucharski 		}
327*1b8adde7SWilliam Kucharski 	}
328*1b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
329*1b8adde7SWilliam Kucharski 	ptr = 0;
330*1b8adde7SWilliam Kucharski 	undi.rom = NULL;
331*1b8adde7SWilliam Kucharski 	return 0;
332*1b8adde7SWilliam Kucharski }
333*1b8adde7SWilliam Kucharski 
334*1b8adde7SWilliam Kucharski /* Locate ROMs containing UNDI drivers.
335*1b8adde7SWilliam Kucharski  */
336*1b8adde7SWilliam Kucharski 
337*1b8adde7SWilliam Kucharski int hunt_undi_rom ( void ) {
338*1b8adde7SWilliam Kucharski 	while ( hunt_rom() ) {
339*1b8adde7SWilliam Kucharski 		if ( undi.rom->undi_rom_id_off == 0 ) {
340*1b8adde7SWilliam Kucharski 			DBG ( "Not a PXE ROM\n" );
341*1b8adde7SWilliam Kucharski 			continue;
342*1b8adde7SWilliam Kucharski 		}
343*1b8adde7SWilliam Kucharski 		undi.undi_rom_id = (undi_rom_id_t *)
344*1b8adde7SWilliam Kucharski 			( (void *)undi.rom + undi.rom->undi_rom_id_off );
345*1b8adde7SWilliam Kucharski 		if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) {
346*1b8adde7SWilliam Kucharski 			DBG ( "Invalid UNDI signature\n" );
347*1b8adde7SWilliam Kucharski 			continue;
348*1b8adde7SWilliam Kucharski 		}
349*1b8adde7SWilliam Kucharski 		printf ( "Revision %d.%d.%d",
350*1b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[2],
351*1b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[1],
352*1b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[0] );
353*1b8adde7SWilliam Kucharski 		return 1;
354*1b8adde7SWilliam Kucharski 	}
355*1b8adde7SWilliam Kucharski 	return 0;
356*1b8adde7SWilliam Kucharski }
357*1b8adde7SWilliam Kucharski 
358*1b8adde7SWilliam Kucharski /**************************************************************************
359*1b8adde7SWilliam Kucharski  * Low-level UNDI API call wrappers
360*1b8adde7SWilliam Kucharski  **************************************************************************/
361*1b8adde7SWilliam Kucharski 
362*1b8adde7SWilliam Kucharski /* Make a real-mode UNDI API call to the UNDI routine at
363*1b8adde7SWilliam Kucharski  * routine_seg:routine_off, passing in three uint16 parameters on the
364*1b8adde7SWilliam Kucharski  * real-mode stack.
365*1b8adde7SWilliam Kucharski  * Calls the assembler wrapper routine __undi_call.
366*1b8adde7SWilliam Kucharski  */
367*1b8adde7SWilliam Kucharski 
368*1b8adde7SWilliam Kucharski static inline PXENV_EXIT_t _undi_call ( uint16_t routine_seg,
369*1b8adde7SWilliam Kucharski 					uint16_t routine_off, uint16_t st0,
370*1b8adde7SWilliam Kucharski 					uint16_t st1, uint16_t st2 ) {
371*1b8adde7SWilliam Kucharski 	PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
372*1b8adde7SWilliam Kucharski 
373*1b8adde7SWilliam Kucharski 	undi.undi_call_info->routine.segment = routine_seg;
374*1b8adde7SWilliam Kucharski 	undi.undi_call_info->routine.offset = routine_off;
375*1b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[0] = st0;
376*1b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[1] = st1;
377*1b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[2] = st2;
378*1b8adde7SWilliam Kucharski 	ret = __undi_call ( SEGMENT( undi.undi_call_info ),
379*1b8adde7SWilliam Kucharski 			    OFFSET( undi.undi_call_info ) );
380*1b8adde7SWilliam Kucharski 
381*1b8adde7SWilliam Kucharski 	/* UNDI API calls may rudely change the status of A20 and not
382*1b8adde7SWilliam Kucharski 	 * bother to restore it afterwards.  Intel is known to be
383*1b8adde7SWilliam Kucharski 	 * guilty of this.
384*1b8adde7SWilliam Kucharski 	 *
385*1b8adde7SWilliam Kucharski 	 * Note that we will return to this point even if A20 gets
386*1b8adde7SWilliam Kucharski 	 * screwed up by the UNDI driver, because Etherboot always
387*1b8adde7SWilliam Kucharski 	 * resides in an even megabyte of RAM.
388*1b8adde7SWilliam Kucharski 	 */
389*1b8adde7SWilliam Kucharski 	gateA20_set();
390*1b8adde7SWilliam Kucharski 
391*1b8adde7SWilliam Kucharski 	return ret;
392*1b8adde7SWilliam Kucharski }
393*1b8adde7SWilliam Kucharski 
394*1b8adde7SWilliam Kucharski /* Make a real-mode call to the UNDI loader routine at
395*1b8adde7SWilliam Kucharski  * routine_seg:routine_off, passing in the seg:off address of a
396*1b8adde7SWilliam Kucharski  * pxenv_structure on the real-mode stack.
397*1b8adde7SWilliam Kucharski  */
398*1b8adde7SWilliam Kucharski 
399*1b8adde7SWilliam Kucharski int undi_call_loader ( void ) {
400*1b8adde7SWilliam Kucharski 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
401*1b8adde7SWilliam Kucharski 
402*1b8adde7SWilliam Kucharski 	pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
403*1b8adde7SWilliam Kucharski 				  undi.undi_rom_id->undi_loader_off,
404*1b8adde7SWilliam Kucharski 				  OFFSET( undi.pxs ),
405*1b8adde7SWilliam Kucharski 				  SEGMENT( undi.pxs ),
406*1b8adde7SWilliam Kucharski 				  0 /* Unused for UNDI loader API */ );
407*1b8adde7SWilliam Kucharski 	/* Return 1 for success, to be consistent with other routines */
408*1b8adde7SWilliam Kucharski 	if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1;
409*1b8adde7SWilliam Kucharski 	DBG ( "UNDI loader call failed with status %#hx\n",
410*1b8adde7SWilliam Kucharski 		 undi.pxs->Status );
411*1b8adde7SWilliam Kucharski 	return 0;
412*1b8adde7SWilliam Kucharski }
413*1b8adde7SWilliam Kucharski 
414*1b8adde7SWilliam Kucharski /* Make a real-mode UNDI API call, passing in the opcode and the
415*1b8adde7SWilliam Kucharski  * seg:off address of a pxenv_structure on the real-mode stack.
416*1b8adde7SWilliam Kucharski  *
417*1b8adde7SWilliam Kucharski  * Two versions: undi_call() will automatically report any failure
418*1b8adde7SWilliam Kucharski  * codes, undi_call_silent() will not.
419*1b8adde7SWilliam Kucharski  */
420*1b8adde7SWilliam Kucharski 
421*1b8adde7SWilliam Kucharski int undi_call_silent ( uint16_t opcode ) {
422*1b8adde7SWilliam Kucharski 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
423*1b8adde7SWilliam Kucharski 
424*1b8adde7SWilliam Kucharski 	pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
425*1b8adde7SWilliam Kucharski 				  undi.pxe->EntryPointSP.offset,
426*1b8adde7SWilliam Kucharski 				  opcode,
427*1b8adde7SWilliam Kucharski 				  OFFSET( undi.pxs ),
428*1b8adde7SWilliam Kucharski 				  SEGMENT( undi.pxs ) );
429*1b8adde7SWilliam Kucharski 	/* Return 1 for success, to be consistent with other routines */
430*1b8adde7SWilliam Kucharski 	return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
431*1b8adde7SWilliam Kucharski }
432*1b8adde7SWilliam Kucharski 
433*1b8adde7SWilliam Kucharski int undi_call ( uint16_t opcode ) {
434*1b8adde7SWilliam Kucharski 	if ( undi_call_silent ( opcode ) ) return 1;
435*1b8adde7SWilliam Kucharski 	DBG ( "UNDI API call %#hx failed with status %#hx\n",
436*1b8adde7SWilliam Kucharski 		 opcode, undi.pxs->Status );
437*1b8adde7SWilliam Kucharski 	return 0;
438*1b8adde7SWilliam Kucharski }
439*1b8adde7SWilliam Kucharski 
440*1b8adde7SWilliam Kucharski /**************************************************************************
441*1b8adde7SWilliam Kucharski  * High-level UNDI API call wrappers
442*1b8adde7SWilliam Kucharski  **************************************************************************/
443*1b8adde7SWilliam Kucharski 
444*1b8adde7SWilliam Kucharski /* Install the UNDI driver from a located UNDI ROM.
445*1b8adde7SWilliam Kucharski  */
446*1b8adde7SWilliam Kucharski 
447*1b8adde7SWilliam Kucharski int undi_loader ( void ) {
448*1b8adde7SWilliam Kucharski 	pxe_t *pxe = NULL;
449*1b8adde7SWilliam Kucharski 
450*1b8adde7SWilliam Kucharski 	/* AX contains PCI bus:devfn (PCI specification) */
451*1b8adde7SWilliam Kucharski 	undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
452*1b8adde7SWilliam Kucharski 	/* BX and DX set to 0xffff for non-ISAPnP devices
453*1b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
454*1b8adde7SWilliam Kucharski 	 */
455*1b8adde7SWilliam Kucharski 	undi.pxs->loader.bx = 0xffff;
456*1b8adde7SWilliam Kucharski 	undi.pxs->loader.dx = 0xffff;
457*1b8adde7SWilliam Kucharski 	/* ES:DI points to PnP BIOS' $PnP structure
458*1b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
459*1b8adde7SWilliam Kucharski 	 */
460*1b8adde7SWilliam Kucharski 	undi.pxs->loader.es = 0xf000;
461*1b8adde7SWilliam Kucharski 	undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
462*1b8adde7SWilliam Kucharski 
463*1b8adde7SWilliam Kucharski 	/* Allocate space for UNDI driver's code and data segments */
464*1b8adde7SWilliam Kucharski 	undi.driver_code_size = undi.undi_rom_id->code_size;
465*1b8adde7SWilliam Kucharski 	undi.driver_code = UNDI_HEAP;
466*1b8adde7SWilliam Kucharski 	if ( undi.driver_code == NULL ) {
467*1b8adde7SWilliam Kucharski 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
468*1b8adde7SWilliam Kucharski 			 undi.driver_code_size );
469*1b8adde7SWilliam Kucharski 		return 0;
470*1b8adde7SWilliam Kucharski 	}
471*1b8adde7SWilliam Kucharski 	undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
472*1b8adde7SWilliam Kucharski 
473*1b8adde7SWilliam Kucharski 	undi.driver_data_size = undi.undi_rom_id->data_size;
474*1b8adde7SWilliam Kucharski 	undi.driver_data = (void *)((((unsigned long)UNDI_HEAP + undi.undi_rom_id->code_size) | (1024 -1)) + 1);
475*1b8adde7SWilliam Kucharski 	if ( undi.driver_data == NULL ) {
476*1b8adde7SWilliam Kucharski 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
477*1b8adde7SWilliam Kucharski 			 undi.driver_data_size );
478*1b8adde7SWilliam Kucharski 		return 0;
479*1b8adde7SWilliam Kucharski 	}
480*1b8adde7SWilliam Kucharski 	undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
481*1b8adde7SWilliam Kucharski 
482*1b8adde7SWilliam Kucharski 	DBG ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
483*1b8adde7SWilliam Kucharski 		undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds );
484*1b8adde7SWilliam Kucharski 
485*1b8adde7SWilliam Kucharski 	/* Do the API call to install the loader */
486*1b8adde7SWilliam Kucharski 	if ( ! undi_call_loader () ) return 0;
487*1b8adde7SWilliam Kucharski 
488*1b8adde7SWilliam Kucharski 	pxe = VIRTUAL( undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
489*1b8adde7SWilliam Kucharski 	DBG ( "UNDI driver created a pixie at %hx:%hx...",
490*1b8adde7SWilliam Kucharski 		 undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
491*1b8adde7SWilliam Kucharski 	if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) {
492*1b8adde7SWilliam Kucharski 		DBG ( "invalid signature\n" );
493*1b8adde7SWilliam Kucharski 		return 0;
494*1b8adde7SWilliam Kucharski 	}
495*1b8adde7SWilliam Kucharski 	if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
496*1b8adde7SWilliam Kucharski 		DBG ( "invalid checksum\n" );
497*1b8adde7SWilliam Kucharski 		return 0;
498*1b8adde7SWilliam Kucharski 	}
499*1b8adde7SWilliam Kucharski 	DBG ( "ok\n" );
500*1b8adde7SWilliam Kucharski 	undi.pxe = pxe;
501*1b8adde7SWilliam Kucharski 	pxe_dump();
502*1b8adde7SWilliam Kucharski 	return 1;
503*1b8adde7SWilliam Kucharski }
504*1b8adde7SWilliam Kucharski 
505*1b8adde7SWilliam Kucharski /* Start the UNDI driver.
506*1b8adde7SWilliam Kucharski  */
507*1b8adde7SWilliam Kucharski 
508*1b8adde7SWilliam Kucharski int eb_pxenv_start_undi ( void ) {
509*1b8adde7SWilliam Kucharski 	int success = 0;
510*1b8adde7SWilliam Kucharski 
511*1b8adde7SWilliam Kucharski 	/* AX contains PCI bus:devfn (PCI specification) */
512*1b8adde7SWilliam Kucharski 	undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
513*1b8adde7SWilliam Kucharski 	/* BX and DX set to 0xffff for non-ISAPnP devices
514*1b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
515*1b8adde7SWilliam Kucharski 	 */
516*1b8adde7SWilliam Kucharski 	undi.pxs->start_undi.bx = 0xffff;
517*1b8adde7SWilliam Kucharski 	undi.pxs->start_undi.dx = 0xffff;
518*1b8adde7SWilliam Kucharski 	/* ES:DI points to PnP BIOS' $PnP structure
519*1b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
520*1b8adde7SWilliam Kucharski 	 */
521*1b8adde7SWilliam Kucharski 	undi.pxs->start_undi.es = 0xf000;
522*1b8adde7SWilliam Kucharski 	undi.pxs->start_undi.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
523*1b8adde7SWilliam Kucharski 
524*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
525*1b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.ax,
526*1b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.bx, undi.pxs->start_undi.dx,
527*1b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.es, undi.pxs->start_undi.di );
528*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_START_UNDI );
529*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
530*1b8adde7SWilliam Kucharski 	if ( success ) undi.prestarted = 1;
531*1b8adde7SWilliam Kucharski 	return success;
532*1b8adde7SWilliam Kucharski }
533*1b8adde7SWilliam Kucharski 
534*1b8adde7SWilliam Kucharski int eb_pxenv_undi_startup ( void )	{
535*1b8adde7SWilliam Kucharski 	int success = 0;
536*1b8adde7SWilliam Kucharski 
537*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
538*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_STARTUP );
539*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
540*1b8adde7SWilliam Kucharski 	if ( success ) undi.started = 1;
541*1b8adde7SWilliam Kucharski 	return success;
542*1b8adde7SWilliam Kucharski }
543*1b8adde7SWilliam Kucharski 
544*1b8adde7SWilliam Kucharski int eb_pxenv_undi_cleanup ( void ) {
545*1b8adde7SWilliam Kucharski 	int success = 0;
546*1b8adde7SWilliam Kucharski 
547*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
548*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_CLEANUP );
549*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
550*1b8adde7SWilliam Kucharski 	return success;
551*1b8adde7SWilliam Kucharski }
552*1b8adde7SWilliam Kucharski 
553*1b8adde7SWilliam Kucharski int eb_pxenv_undi_initialize ( void ) {
554*1b8adde7SWilliam Kucharski 	int success = 0;
555*1b8adde7SWilliam Kucharski 
556*1b8adde7SWilliam Kucharski 	undi.pxs->undi_initialize.ProtocolIni = 0;
557*1b8adde7SWilliam Kucharski 	memset ( &undi.pxs->undi_initialize.reserved, 0,
558*1b8adde7SWilliam Kucharski 		 sizeof ( undi.pxs->undi_initialize.reserved ) );
559*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
560*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_INITIALIZE );
561*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
562*1b8adde7SWilliam Kucharski 	if ( success ) undi.initialized = 1;
563*1b8adde7SWilliam Kucharski 	return success;
564*1b8adde7SWilliam Kucharski }
565*1b8adde7SWilliam Kucharski 
566*1b8adde7SWilliam Kucharski int eb_pxenv_undi_shutdown ( void ) {
567*1b8adde7SWilliam Kucharski 	int success = 0;
568*1b8adde7SWilliam Kucharski 
569*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
570*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_SHUTDOWN );
571*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
572*1b8adde7SWilliam Kucharski 	if ( success ) {
573*1b8adde7SWilliam Kucharski 		undi.initialized = 0;
574*1b8adde7SWilliam Kucharski 		undi.started = 0;
575*1b8adde7SWilliam Kucharski 	}
576*1b8adde7SWilliam Kucharski 	return success;
577*1b8adde7SWilliam Kucharski }
578*1b8adde7SWilliam Kucharski 
579*1b8adde7SWilliam Kucharski int eb_pxenv_undi_open ( void ) {
580*1b8adde7SWilliam Kucharski 	int success = 0;
581*1b8adde7SWilliam Kucharski 
582*1b8adde7SWilliam Kucharski 	undi.pxs->undi_open.OpenFlag = 0;
583*1b8adde7SWilliam Kucharski 	undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
584*1b8adde7SWilliam Kucharski 
585*1b8adde7SWilliam Kucharski 	/* Multicast support not yet implemented */
586*1b8adde7SWilliam Kucharski 	undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0;
587*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
588*1b8adde7SWilliam Kucharski 	      "MCastAddrCount=%hx\n",
589*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter,
590*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount );
591*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_OPEN );
592*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
593*1b8adde7SWilliam Kucharski 	if ( success ) undi.opened = 1;
594*1b8adde7SWilliam Kucharski 	return success;
595*1b8adde7SWilliam Kucharski }
596*1b8adde7SWilliam Kucharski 
597*1b8adde7SWilliam Kucharski int eb_pxenv_undi_close ( void ) {
598*1b8adde7SWilliam Kucharski 	int success = 0;
599*1b8adde7SWilliam Kucharski 
600*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
601*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_CLOSE );
602*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
603*1b8adde7SWilliam Kucharski 	if ( success ) undi.opened = 0;
604*1b8adde7SWilliam Kucharski 	return success;
605*1b8adde7SWilliam Kucharski }
606*1b8adde7SWilliam Kucharski 
607*1b8adde7SWilliam Kucharski int eb_pxenv_undi_transmit_packet ( void ) {
608*1b8adde7SWilliam Kucharski 	int success = 0;
609*1b8adde7SWilliam Kucharski 	static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
610*1b8adde7SWilliam Kucharski 
611*1b8adde7SWilliam Kucharski 	/* XMitFlag selects unicast / broadcast */
612*1b8adde7SWilliam Kucharski 	if ( memcmp ( undi.xmit_data->destaddr, broadcast,
613*1b8adde7SWilliam Kucharski 		      sizeof(broadcast) ) == 0 ) {
614*1b8adde7SWilliam Kucharski 		undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST;
615*1b8adde7SWilliam Kucharski 	} else {
616*1b8adde7SWilliam Kucharski 		undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
617*1b8adde7SWilliam Kucharski 	}
618*1b8adde7SWilliam Kucharski 
619*1b8adde7SWilliam Kucharski 	/* Zero reserved dwords */
620*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.Reserved[0] = 0;
621*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.Reserved[1] = 0;
622*1b8adde7SWilliam Kucharski 
623*1b8adde7SWilliam Kucharski 	/* Segment:offset pointer to DestAddr in base memory */
624*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.DestAddr.segment =
625*1b8adde7SWilliam Kucharski 		SEGMENT( undi.xmit_data->destaddr );
626*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.DestAddr.offset =
627*1b8adde7SWilliam Kucharski 		OFFSET( undi.xmit_data->destaddr );
628*1b8adde7SWilliam Kucharski 
629*1b8adde7SWilliam Kucharski 	/* Segment:offset pointer to TBD in base memory */
630*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd );
631*1b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd );
632*1b8adde7SWilliam Kucharski 
633*1b8adde7SWilliam Kucharski 	/* Use only the "immediate" part of the TBD */
634*1b8adde7SWilliam Kucharski 	undi.xmit_data->tbd.DataBlkCount = 0;
635*1b8adde7SWilliam Kucharski 
636*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
637*1b8adde7SWilliam Kucharski 	      "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
638*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.Protocol,
639*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.XmitFlag,
640*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.DestAddr.segment,
641*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.DestAddr.offset,
642*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.TBD.segment,
643*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.TBD.offset );
644*1b8adde7SWilliam Kucharski 	DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
645*1b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.ImmedLength,
646*1b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.Xmit.segment,
647*1b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.Xmit.offset,
648*1b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.DataBlkCount );
649*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_TRANSMIT );
650*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
651*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs) );
652*1b8adde7SWilliam Kucharski 	return success;
653*1b8adde7SWilliam Kucharski }
654*1b8adde7SWilliam Kucharski 
655*1b8adde7SWilliam Kucharski int eb_pxenv_undi_set_station_address ( void ) {
656*1b8adde7SWilliam Kucharski 	/* This will spuriously fail on some cards.  Ignore failures.
657*1b8adde7SWilliam Kucharski 	 * We only ever use it to set the MAC address to the card's
658*1b8adde7SWilliam Kucharski 	 * permanent value anyway, so it's a useless call (although we
659*1b8adde7SWilliam Kucharski 	 * make it because PXE spec says we should).
660*1b8adde7SWilliam Kucharski 	 */
661*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
662*1b8adde7SWilliam Kucharski 	      "StationAddress=%!\n",
663*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_set_station_address.StationAddress );
664*1b8adde7SWilliam Kucharski 	undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS );
665*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
666*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs) );
667*1b8adde7SWilliam Kucharski 	return 1;
668*1b8adde7SWilliam Kucharski }
669*1b8adde7SWilliam Kucharski 
670*1b8adde7SWilliam Kucharski int eb_pxenv_undi_get_information ( void ) {
671*1b8adde7SWilliam Kucharski 	int success = 0;
672*1b8adde7SWilliam Kucharski 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
673*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
674*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_GET_INFORMATION );
675*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
676*1b8adde7SWilliam Kucharski 	      "BaseIO=%hx IntNumber=%hx ...\n"
677*1b8adde7SWilliam Kucharski 	      "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
678*1b8adde7SWilliam Kucharski 	      "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
679*1b8adde7SWilliam Kucharski 	      "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
680*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
681*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.BaseIo,
682*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.IntNumber,
683*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.MaxTranUnit,
684*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.HwType,
685*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.HwAddrLen,
686*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.CurrentNodeAddress,
687*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.PermNodeAddress,
688*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.ROMAddress,
689*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.RxBufCt,
690*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.TxBufCt );
691*1b8adde7SWilliam Kucharski 	return success;
692*1b8adde7SWilliam Kucharski }
693*1b8adde7SWilliam Kucharski 
694*1b8adde7SWilliam Kucharski int eb_pxenv_undi_get_iface_info ( void ) {
695*1b8adde7SWilliam Kucharski 	int success = 0;
696*1b8adde7SWilliam Kucharski 
697*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
698*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_GET_IFACE_INFO );
699*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
700*1b8adde7SWilliam Kucharski 	      "... LinkSpeed=%x ServiceFlags=%x\n",
701*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
702*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.IfaceType,
703*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.LinkSpeed,
704*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.ServiceFlags );
705*1b8adde7SWilliam Kucharski 	return success;
706*1b8adde7SWilliam Kucharski }
707*1b8adde7SWilliam Kucharski 
708*1b8adde7SWilliam Kucharski int eb_pxenv_undi_isr ( void ) {
709*1b8adde7SWilliam Kucharski 	int success = 0;
710*1b8adde7SWilliam Kucharski 
711*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
712*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FuncFlag );
713*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_ISR );
714*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
715*1b8adde7SWilliam Kucharski 	      "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
716*1b8adde7SWilliam Kucharski 	      "ProtType=%hhx ...\n... PktType=%hhx\n",
717*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag,
718*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.BufferLength,
719*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FrameLength,
720*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FrameHeaderLength,
721*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.Frame.segment,
722*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.Frame.offset,
723*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.ProtType,
724*1b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.PktType );
725*1b8adde7SWilliam Kucharski 	return success;
726*1b8adde7SWilliam Kucharski }
727*1b8adde7SWilliam Kucharski 
728*1b8adde7SWilliam Kucharski int eb_pxenv_stop_undi ( void ) {
729*1b8adde7SWilliam Kucharski 	int success = 0;
730*1b8adde7SWilliam Kucharski 
731*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_UNDI => (void)\n" );
732*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_STOP_UNDI );
733*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
734*1b8adde7SWilliam Kucharski 	if ( success ) undi.prestarted = 0;
735*1b8adde7SWilliam Kucharski 	return success;
736*1b8adde7SWilliam Kucharski }
737*1b8adde7SWilliam Kucharski 
738*1b8adde7SWilliam Kucharski int eb_pxenv_unload_stack ( void ) {
739*1b8adde7SWilliam Kucharski 	int success = 0;
740*1b8adde7SWilliam Kucharski 
741*1b8adde7SWilliam Kucharski 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
742*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
743*1b8adde7SWilliam Kucharski 	success = undi_call_silent ( PXENV_UNLOAD_STACK );
744*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
745*1b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
746*1b8adde7SWilliam Kucharski 	      ( undi.pxs->Status == PXENV_STATUS_SUCCESS ?
747*1b8adde7SWilliam Kucharski 		"base-code is ready to be removed" :
748*1b8adde7SWilliam Kucharski 		( undi.pxs->Status == PXENV_STATUS_FAILURE ?
749*1b8adde7SWilliam Kucharski 		  "the size of free base memory has been changed" :
750*1b8adde7SWilliam Kucharski 		  ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ?
751*1b8adde7SWilliam Kucharski 		    "the NIC interrupt vector has been changed" :
752*1b8adde7SWilliam Kucharski 		    "UNEXPECTED STATUS CODE" ) ) ) );
753*1b8adde7SWilliam Kucharski 	return success;
754*1b8adde7SWilliam Kucharski }
755*1b8adde7SWilliam Kucharski 
756*1b8adde7SWilliam Kucharski int eb_pxenv_stop_base ( void ) {
757*1b8adde7SWilliam Kucharski 	int success = 0;
758*1b8adde7SWilliam Kucharski 
759*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_BASE => (void)\n" );
760*1b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_STOP_BASE );
761*1b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
762*1b8adde7SWilliam Kucharski 	return success;
763*1b8adde7SWilliam Kucharski }
764*1b8adde7SWilliam Kucharski 
765*1b8adde7SWilliam Kucharski /* Unload UNDI base code (if any present) and free memory.
766*1b8adde7SWilliam Kucharski  */
767*1b8adde7SWilliam Kucharski int undi_unload_base_code ( void ) {
768*1b8adde7SWilliam Kucharski 	/* In GRUB, we do not allocate anything, but we still can call
769*1b8adde7SWilliam Kucharski 	 * to free the base space */
770*1b8adde7SWilliam Kucharski 	void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 );
771*1b8adde7SWilliam Kucharski 	size_t bc_code_size = undi.pxe->BC_Code.Seg_Size;
772*1b8adde7SWilliam Kucharski 	void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 );
773*1b8adde7SWilliam Kucharski 	size_t bc_data_size = undi.pxe->BC_Data.Seg_Size;
774*1b8adde7SWilliam Kucharski 	void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 );
775*1b8adde7SWilliam Kucharski 	size_t bc_stck_size = undi.pxe->Stack.Seg_Size;
776*1b8adde7SWilliam Kucharski 	firing_squad_lineup_t lineup;
777*1b8adde7SWilliam Kucharski 
778*1b8adde7SWilliam Kucharski 	/* Don't unload if there is no base code present */
779*1b8adde7SWilliam Kucharski 	if ( undi.pxe->BC_Code.Seg_Addr == 0 ) return 1;
780*1b8adde7SWilliam Kucharski 
781*1b8adde7SWilliam Kucharski 	/* Since we never start the base code, the only time we should
782*1b8adde7SWilliam Kucharski 	 * reach this is if we were loaded via PXE.  There are many
783*1b8adde7SWilliam Kucharski 	 * different and conflicting versions of the "correct" way to
784*1b8adde7SWilliam Kucharski 	 * unload the PXE base code, several of which appear within
785*1b8adde7SWilliam Kucharski 	 * the PXE specification itself.  This one seems to work for
786*1b8adde7SWilliam Kucharski 	 * our purposes.
787*1b8adde7SWilliam Kucharski 	 */
788*1b8adde7SWilliam Kucharski 	eb_pxenv_stop_base();
789*1b8adde7SWilliam Kucharski 	//eb_pxenv_unload_stack();
790*1b8adde7SWilliam Kucharski /*	if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) &&
791*1b8adde7SWilliam Kucharski 	     ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) ) {
792*1b8adde7SWilliam Kucharski 		printf ( "Could not free memory allocated to PXE base code: "
793*1b8adde7SWilliam Kucharski 			 "possible memory leak\n" );
794*1b8adde7SWilliam Kucharski 		return 0;
795*1b8adde7SWilliam Kucharski 		}*/
796*1b8adde7SWilliam Kucharski 	/* Free data structures.  Forget what the PXE specification
797*1b8adde7SWilliam Kucharski 	 * says about how to calculate the new size of base memory;
798*1b8adde7SWilliam Kucharski 	 * basemem.c takes care of all that for us.  Note that we also
799*1b8adde7SWilliam Kucharski 	 * have to free the stack (even though PXE spec doesn't say
800*1b8adde7SWilliam Kucharski 	 * anything about it) because nothing else is going to do so.
801*1b8adde7SWilliam Kucharski 	 *
802*1b8adde7SWilliam Kucharski 	 * Structures will almost certainly not be kB-aligned and
803*1b8adde7SWilliam Kucharski 	 * there's a reasonable chance that the UNDI code or data
804*1b8adde7SWilliam Kucharski 	 * portions will lie in the same kB as the base code.  Since
805*1b8adde7SWilliam Kucharski 	 * forget_base_memory works only in 1kB increments, this means
806*1b8adde7SWilliam Kucharski 	 * we have to do some arcane trickery.
807*1b8adde7SWilliam Kucharski 	 */
808*1b8adde7SWilliam Kucharski 	memset ( &lineup, 0, sizeof(lineup) );
809*1b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_code) != 0 )
810*1b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT );
811*1b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_data) != 0 )
812*1b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT );
813*1b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_stck) != 0 )
814*1b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT );
815*1b8adde7SWilliam Kucharski 	/* Don't shoot any bits of the UNDI driver code or data */
816*1b8adde7SWilliam Kucharski 	assemble_firing_squad ( &lineup,
817*1b8adde7SWilliam Kucharski 				VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0),
818*1b8adde7SWilliam Kucharski 				undi.pxe->UNDICode.Seg_Size, DONTSHOOT );
819*1b8adde7SWilliam Kucharski 	assemble_firing_squad ( &lineup,
820*1b8adde7SWilliam Kucharski 				VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0),
821*1b8adde7SWilliam Kucharski 				undi.pxe->UNDIData.Seg_Size, DONTSHOOT );
822*1b8adde7SWilliam Kucharski 	//shoot_targets ( &lineup );
823*1b8adde7SWilliam Kucharski 	//undi.pxe->BC_Code.Seg_Addr = 0;
824*1b8adde7SWilliam Kucharski 	//undi.pxe->BC_Data.Seg_Addr = 0;
825*1b8adde7SWilliam Kucharski 	//undi.pxe->Stack.Seg_Addr = 0;
826*1b8adde7SWilliam Kucharski 
827*1b8adde7SWilliam Kucharski 	/* Free and reallocate our own base memory data structures, to
828*1b8adde7SWilliam Kucharski 	 * allow the freed base-code blocks to be fully released.
829*1b8adde7SWilliam Kucharski 	 */
830*1b8adde7SWilliam Kucharski 	free_base_mem_data();
831*1b8adde7SWilliam Kucharski 	if ( ! allocate_base_mem_data() ) {
832*1b8adde7SWilliam Kucharski 		printf ( "FATAL: memory unaccountably lost\n" );
833*1b8adde7SWilliam Kucharski 		while ( 1 ) {};
834*1b8adde7SWilliam Kucharski 	}
835*1b8adde7SWilliam Kucharski 
836*1b8adde7SWilliam Kucharski 	return 1;
837*1b8adde7SWilliam Kucharski }
838*1b8adde7SWilliam Kucharski 
839*1b8adde7SWilliam Kucharski /* UNDI full initialization
840*1b8adde7SWilliam Kucharski  *
841*1b8adde7SWilliam Kucharski  * This calls all the various UNDI initialization routines in sequence.
842*1b8adde7SWilliam Kucharski  */
843*1b8adde7SWilliam Kucharski 
844*1b8adde7SWilliam Kucharski int undi_full_startup ( void ) {
845*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_start_undi() ) return 0;
846*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_startup() ) return 0;
847*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_initialize() ) return 0;
848*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_get_information() ) return 0;
849*1b8adde7SWilliam Kucharski 	undi.irq = undi.pxs->undi_get_information.IntNumber;
850*1b8adde7SWilliam Kucharski 	if ( ! install_undi_irq_handler ( undi.irq, undi.pxe->EntryPointSP ) ) {
851*1b8adde7SWilliam Kucharski 		undi.irq = IRQ_NONE;
852*1b8adde7SWilliam Kucharski 		return 0;
853*1b8adde7SWilliam Kucharski 	}
854*1b8adde7SWilliam Kucharski 	memmove ( &undi.pxs->undi_set_station_address.StationAddress,
855*1b8adde7SWilliam Kucharski 		  &undi.pxs->undi_get_information.PermNodeAddress,
856*1b8adde7SWilliam Kucharski 		  sizeof (undi.pxs->undi_set_station_address.StationAddress) );
857*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_set_station_address() ) return 0;
858*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_open() ) return 0;
859*1b8adde7SWilliam Kucharski 	/* install_undi_irq_handler leaves irq disabled */
860*1b8adde7SWilliam Kucharski 	enable_irq ( undi.irq );
861*1b8adde7SWilliam Kucharski 	return 1;
862*1b8adde7SWilliam Kucharski }
863*1b8adde7SWilliam Kucharski 
864*1b8adde7SWilliam Kucharski /* UNDI full shutdown
865*1b8adde7SWilliam Kucharski  *
866*1b8adde7SWilliam Kucharski  * This calls all the various UNDI shutdown routines in sequence and
867*1b8adde7SWilliam Kucharski  * also frees any memory that it can.
868*1b8adde7SWilliam Kucharski  */
869*1b8adde7SWilliam Kucharski 
870*1b8adde7SWilliam Kucharski int undi_full_shutdown ( void ) {
871*1b8adde7SWilliam Kucharski 	if ( undi.pxe != NULL ) {
872*1b8adde7SWilliam Kucharski 		/* In case we didn't allocate the driver's memory in the first
873*1b8adde7SWilliam Kucharski 		 * place, try to grab the code and data segments and sizes
874*1b8adde7SWilliam Kucharski 		 * from the !PXE structure.
875*1b8adde7SWilliam Kucharski 		 */
876*1b8adde7SWilliam Kucharski 		if ( undi.driver_code == NULL ) {
877*1b8adde7SWilliam Kucharski 			undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
878*1b8adde7SWilliam Kucharski 						   0 );
879*1b8adde7SWilliam Kucharski 			undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
880*1b8adde7SWilliam Kucharski 		}
881*1b8adde7SWilliam Kucharski 		if ( undi.driver_data == NULL ) {
882*1b8adde7SWilliam Kucharski 			undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
883*1b8adde7SWilliam Kucharski 						   0 );
884*1b8adde7SWilliam Kucharski 			undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
885*1b8adde7SWilliam Kucharski 		}
886*1b8adde7SWilliam Kucharski 
887*1b8adde7SWilliam Kucharski 		/* Ignore errors and continue in the hope of shutting
888*1b8adde7SWilliam Kucharski 		 * down anyway
889*1b8adde7SWilliam Kucharski 		 */
890*1b8adde7SWilliam Kucharski 		if ( undi.opened ) eb_pxenv_undi_close();
891*1b8adde7SWilliam Kucharski 		if ( undi.started ) {
892*1b8adde7SWilliam Kucharski 			eb_pxenv_undi_cleanup();
893*1b8adde7SWilliam Kucharski 			/* We may get spurious UNDI API errors at this
894*1b8adde7SWilliam Kucharski 			 * point.  If startup() succeeded but
895*1b8adde7SWilliam Kucharski 			 * initialize() failed then according to the
896*1b8adde7SWilliam Kucharski 			 * spec, we should call shutdown().  However,
897*1b8adde7SWilliam Kucharski 			 * some NICS will fail with a status code
898*1b8adde7SWilliam Kucharski 			 * 0x006a (INVALID_STATE).
899*1b8adde7SWilliam Kucharski 			 */
900*1b8adde7SWilliam Kucharski 			eb_pxenv_undi_shutdown();
901*1b8adde7SWilliam Kucharski 		}
902*1b8adde7SWilliam Kucharski 		if ( undi.irq != IRQ_NONE ) {
903*1b8adde7SWilliam Kucharski 			remove_undi_irq_handler ( undi.irq );
904*1b8adde7SWilliam Kucharski 			undi.irq = IRQ_NONE;
905*1b8adde7SWilliam Kucharski 		}
906*1b8adde7SWilliam Kucharski 		undi_unload_base_code();
907*1b8adde7SWilliam Kucharski 		if ( undi.prestarted ) {
908*1b8adde7SWilliam Kucharski 			eb_pxenv_stop_undi();
909*1b8adde7SWilliam Kucharski 			/* Success OR Failure indicates that memory
910*1b8adde7SWilliam Kucharski 			 * can be freed.  Any other status code means
911*1b8adde7SWilliam Kucharski 			 * that it can't.
912*1b8adde7SWilliam Kucharski 			 */
913*1b8adde7SWilliam Kucharski 			if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) ||
914*1b8adde7SWilliam Kucharski 			    ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) {
915*1b8adde7SWilliam Kucharski 				printf ("Could not free memory allocated to "
916*1b8adde7SWilliam Kucharski 					"UNDI driver: possible memory leak\n");
917*1b8adde7SWilliam Kucharski 				return 0;
918*1b8adde7SWilliam Kucharski 			}
919*1b8adde7SWilliam Kucharski 		}
920*1b8adde7SWilliam Kucharski 	}
921*1b8adde7SWilliam Kucharski 	/* Free memory allocated to UNDI driver */
922*1b8adde7SWilliam Kucharski 	if ( undi.driver_code != NULL ) {
923*1b8adde7SWilliam Kucharski 		/* Clear contents in order to eliminate !PXE and PXENV
924*1b8adde7SWilliam Kucharski 		 * signatures to prevent spurious detection via base
925*1b8adde7SWilliam Kucharski 		 * memory scan.
926*1b8adde7SWilliam Kucharski 		 */
927*1b8adde7SWilliam Kucharski 		memset ( undi.driver_code, 0, undi.driver_code_size );
928*1b8adde7SWilliam Kucharski 		/* forget_base_memory ( undi.driver_code, undi.driver_code_size ); */
929*1b8adde7SWilliam Kucharski 		undi.driver_code = NULL;
930*1b8adde7SWilliam Kucharski 		undi.driver_code_size = 0;
931*1b8adde7SWilliam Kucharski 	}
932*1b8adde7SWilliam Kucharski 	if ( undi.driver_data != NULL ) {
933*1b8adde7SWilliam Kucharski 		/* forget_base_memory ( undi.driver_data, undi.driver_data_size ); */
934*1b8adde7SWilliam Kucharski 		undi.driver_data = NULL;
935*1b8adde7SWilliam Kucharski 		undi.driver_data_size = 0;
936*1b8adde7SWilliam Kucharski 	}
937*1b8adde7SWilliam Kucharski 	/* !PXE structure now gone; memory freed */
938*1b8adde7SWilliam Kucharski 	undi.pxe = NULL;
939*1b8adde7SWilliam Kucharski 	return 1;
940*1b8adde7SWilliam Kucharski }
941*1b8adde7SWilliam Kucharski 
942*1b8adde7SWilliam Kucharski /**************************************************************************
943*1b8adde7SWilliam Kucharski POLL - Wait for a frame
944*1b8adde7SWilliam Kucharski ***************************************************************************/
945*1b8adde7SWilliam Kucharski static int undi_poll(struct nic *nic, int retrieve)
946*1b8adde7SWilliam Kucharski {
947*1b8adde7SWilliam Kucharski 	/* Fun, fun, fun.  UNDI drivers don't use polling; they use
948*1b8adde7SWilliam Kucharski 	 * interrupts.  We therefore cheat and pretend that an
949*1b8adde7SWilliam Kucharski 	 * interrupt has occurred every time undi_poll() is called.
950*1b8adde7SWilliam Kucharski 	 * This isn't too much of a hack; PCI devices share IRQs and
951*1b8adde7SWilliam Kucharski 	 * so the first thing that a proper ISR should do is call
952*1b8adde7SWilliam Kucharski 	 * PXENV_UNDI_ISR to determine whether or not the UNDI NIC
953*1b8adde7SWilliam Kucharski 	 * generated the interrupt; there is no harm done by spurious
954*1b8adde7SWilliam Kucharski 	 * calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
955*1b8adde7SWilliam Kucharski 	 * handling them any more rapidly than the usual rate of
956*1b8adde7SWilliam Kucharski 	 * undi_poll() being called even if we did implement a full
957*1b8adde7SWilliam Kucharski 	 * ISR.  So it should work.  Ha!
958*1b8adde7SWilliam Kucharski 	 *
959*1b8adde7SWilliam Kucharski 	 * Addendum (21/10/03).  Some cards don't play nicely with
960*1b8adde7SWilliam Kucharski 	 * this trick, so instead of doing it the easy way we have to
961*1b8adde7SWilliam Kucharski 	 * go to all the hassle of installing a genuine interrupt
962*1b8adde7SWilliam Kucharski 	 * service routine and dealing with the wonderful 8259
963*1b8adde7SWilliam Kucharski 	 * Programmable Interrupt Controller.  Joy.
964*1b8adde7SWilliam Kucharski 	 *
965*1b8adde7SWilliam Kucharski 	 * (02/01/2005). A real UNDI ISR is now implemented in,
966*1b8adde7SWilliam Kucharski 	 * following Figure 3-4 in PXE spec 2.0.  The interrupt
967*1b8adde7SWilliam Kucharski 	 * handler, undi_irq_handler, issues PXENV_UNDI_ISR_IN_START.
968*1b8adde7SWilliam Kucharski 	 * If the interrupt is ours, the handler sends EOI and bumps the
969*1b8adde7SWilliam Kucharski 	 * undi_irq_trigger_count. This polled routine is equivalent
970*1b8adde7SWilliam Kucharski 	 * to the "driver strategy routine".
971*1b8adde7SWilliam Kucharski 	 *
972*1b8adde7SWilliam Kucharski 	 * Another issue is that upper layer await_*() does not handle
973*1b8adde7SWilliam Kucharski 	 * coalesced packets. The UNDI implementation on broadcom chips
974*1b8adde7SWilliam Kucharski 	 * appear to combine interrupts. If we loop through GET_NEXT,
975*1b8adde7SWilliam Kucharski 	 * we may hand up coalesced packets, resulting in drops, and
976*1b8adde7SWilliam Kucharski 	 * severe time delay. As a temperary hack, we return as soon as
977*1b8adde7SWilliam Kucharski 	 * we get something, remembering where we were (IN_PROCESS
978*1b8adde7SWilliam Kucharski 	 * or GET_NEXT). This assume packets are never broken up.
979*1b8adde7SWilliam Kucharski 	 * XXX Need to fix upper layer to handle coalesced data.
980*1b8adde7SWilliam Kucharski 	 */
981*1b8adde7SWilliam Kucharski 
982*1b8adde7SWilliam Kucharski 	static int undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
983*1b8adde7SWilliam Kucharski 
984*1b8adde7SWilliam Kucharski 	/* See if a hardware interrupt has occurred since the last poll().
985*1b8adde7SWilliam Kucharski 	 */
986*1b8adde7SWilliam Kucharski 	switch ( undi_opcode ) {
987*1b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_IN_PROCESS:
988*1b8adde7SWilliam Kucharski 		if ( ! undi_irq_triggered ( undi.irq ) )
989*1b8adde7SWilliam Kucharski 			return 0;
990*1b8adde7SWilliam Kucharski 	default:
991*1b8adde7SWilliam Kucharski 		break;
992*1b8adde7SWilliam Kucharski 	}
993*1b8adde7SWilliam Kucharski 
994*1b8adde7SWilliam Kucharski 	/* We have an interrupt or there is something left from
995*1b8adde7SWilliam Kucharski 	 * last poll. Either way, we need to call UNDI ISR.
996*1b8adde7SWilliam Kucharski 	 */
997*1b8adde7SWilliam Kucharski 	nic->packetlen = 0;
998*1b8adde7SWilliam Kucharski 	undi.pxs->undi_isr.FuncFlag = undi_opcode;
999*1b8adde7SWilliam Kucharski 	/* there is no good way to handle this error */
1000*1b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_isr() ) {
1001*1b8adde7SWilliam Kucharski 		printf ("undi isr call failed: opcode = %d\n", undi_opcode);
1002*1b8adde7SWilliam Kucharski 		return 0;
1003*1b8adde7SWilliam Kucharski 	}
1004*1b8adde7SWilliam Kucharski 	switch ( undi.pxs->undi_isr.FuncFlag ) {
1005*1b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_DONE:
1006*1b8adde7SWilliam Kucharski 		/* Set opcode back to IN_PROCESS and wait for next intr */
1007*1b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
1008*1b8adde7SWilliam Kucharski 		return 0;
1009*1b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_TRANSMIT:
1010*1b8adde7SWilliam Kucharski 		/* We really don't care about transmission complete
1011*1b8adde7SWilliam Kucharski 		 * interrupts. Move on to next frame.
1012*1b8adde7SWilliam Kucharski 		 */
1013*1b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
1014*1b8adde7SWilliam Kucharski 		return 0;
1015*1b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_BUSY:
1016*1b8adde7SWilliam Kucharski 		/* This should never happen.
1017*1b8adde7SWilliam Kucharski 		 */
1018*1b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
1019*1b8adde7SWilliam Kucharski 		printf ( "UNDI ISR thinks it's being re-entered!\n"
1020*1b8adde7SWilliam Kucharski 			 "Aborting receive\n" );
1021*1b8adde7SWilliam Kucharski 		return 0;
1022*1b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_RECEIVE:
1023*1b8adde7SWilliam Kucharski 		/* Copy data to receive buffer and move on to next frame */
1024*1b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
1025*1b8adde7SWilliam Kucharski 		memcpy ( nic->packet + nic->packetlen,
1026*1b8adde7SWilliam Kucharski 			 VIRTUAL( undi.pxs->undi_isr.Frame.segment,
1027*1b8adde7SWilliam Kucharski 				  undi.pxs->undi_isr.Frame.offset ),
1028*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_isr.BufferLength );
1029*1b8adde7SWilliam Kucharski 		nic->packetlen += undi.pxs->undi_isr.BufferLength;
1030*1b8adde7SWilliam Kucharski 		break;
1031*1b8adde7SWilliam Kucharski 	default:
1032*1b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
1033*1b8adde7SWilliam Kucharski 		printf ( "UNDI ISR returned bizzare status code %d\n",
1034*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_isr.FuncFlag );
1035*1b8adde7SWilliam Kucharski 	}
1036*1b8adde7SWilliam Kucharski 
1037*1b8adde7SWilliam Kucharski 	return nic->packetlen > 0 ? 1 : 0;
1038*1b8adde7SWilliam Kucharski }
1039*1b8adde7SWilliam Kucharski 
1040*1b8adde7SWilliam Kucharski /**************************************************************************
1041*1b8adde7SWilliam Kucharski TRANSMIT - Transmit a frame
1042*1b8adde7SWilliam Kucharski ***************************************************************************/
1043*1b8adde7SWilliam Kucharski static void undi_transmit(
1044*1b8adde7SWilliam Kucharski 	struct nic *nic,
1045*1b8adde7SWilliam Kucharski 	const char *d,			/* Destination */
1046*1b8adde7SWilliam Kucharski 	unsigned int t,			/* Type */
1047*1b8adde7SWilliam Kucharski 	unsigned int s,			/* size */
1048*1b8adde7SWilliam Kucharski 	const char *p)			/* Packet */
1049*1b8adde7SWilliam Kucharski {
1050*1b8adde7SWilliam Kucharski 	/* Inhibit compiler warning about unused parameter nic */
1051*1b8adde7SWilliam Kucharski 	if ( nic == NULL ) {};
1052*1b8adde7SWilliam Kucharski 
1053*1b8adde7SWilliam Kucharski 	/* Copy destination to buffer in base memory */
1054*1b8adde7SWilliam Kucharski 	memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
1055*1b8adde7SWilliam Kucharski 
1056*1b8adde7SWilliam Kucharski 	/* Translate packet type to UNDI packet type */
1057*1b8adde7SWilliam Kucharski 	switch ( t ) {
1058*1b8adde7SWilliam Kucharski 	case IP :  undi.pxs->undi_transmit.Protocol = P_IP;   break;
1059*1b8adde7SWilliam Kucharski 	case ARP:  undi.pxs->undi_transmit.Protocol = P_ARP;  break;
1060*1b8adde7SWilliam Kucharski 	case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break;
1061*1b8adde7SWilliam Kucharski 	default: undi.pxs->undi_transmit.Protocol = P_UNKNOWN; break;
1062*1b8adde7SWilliam Kucharski 	}
1063*1b8adde7SWilliam Kucharski 
1064*1b8adde7SWilliam Kucharski 	/* Store packet length in TBD */
1065*1b8adde7SWilliam Kucharski 	undi.xmit_data->tbd.ImmedLength = s;
1066*1b8adde7SWilliam Kucharski 
1067*1b8adde7SWilliam Kucharski 	/* Check to see if data to be transmitted is currently in base
1068*1b8adde7SWilliam Kucharski 	 * memory.  If not, allocate temporary storage in base memory
1069*1b8adde7SWilliam Kucharski 	 * and copy it there.
1070*1b8adde7SWilliam Kucharski 	 */
1071*1b8adde7SWilliam Kucharski 	if ( SEGMENT( p ) <= 0xffff ) {
1072*1b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
1073*1b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
1074*1b8adde7SWilliam Kucharski 	} else {
1075*1b8adde7SWilliam Kucharski 		memcpy ( undi.xmit_buffer, p, s );
1076*1b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer );
1077*1b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer );
1078*1b8adde7SWilliam Kucharski 	}
1079*1b8adde7SWilliam Kucharski 
1080*1b8adde7SWilliam Kucharski 	eb_pxenv_undi_transmit_packet();
1081*1b8adde7SWilliam Kucharski }
1082*1b8adde7SWilliam Kucharski 
1083*1b8adde7SWilliam Kucharski /**************************************************************************
1084*1b8adde7SWilliam Kucharski DISABLE - Turn off ethernet interface
1085*1b8adde7SWilliam Kucharski ***************************************************************************/
1086*1b8adde7SWilliam Kucharski static void undi_disable(struct dev *dev)
1087*1b8adde7SWilliam Kucharski {
1088*1b8adde7SWilliam Kucharski 	/* Inhibit compiler warning about unused parameter dev */
1089*1b8adde7SWilliam Kucharski 	if ( dev == NULL ) {};
1090*1b8adde7SWilliam Kucharski 	undi_full_shutdown();
1091*1b8adde7SWilliam Kucharski 	free_base_mem_data();
1092*1b8adde7SWilliam Kucharski }
1093*1b8adde7SWilliam Kucharski 
1094*1b8adde7SWilliam Kucharski /**************************************************************************
1095*1b8adde7SWilliam Kucharski PROBE - Look for an adapter, this routine's visible to the outside
1096*1b8adde7SWilliam Kucharski ***************************************************************************/
1097*1b8adde7SWilliam Kucharski 
1098*1b8adde7SWilliam Kucharski /* Locate an UNDI driver by first scanning through base memory for an
1099*1b8adde7SWilliam Kucharski  * installed driver and then by scanning for UNDI ROMs and attempting
1100*1b8adde7SWilliam Kucharski  * to install their drivers.
1101*1b8adde7SWilliam Kucharski  */
1102*1b8adde7SWilliam Kucharski 
1103*1b8adde7SWilliam Kucharski int hunt_pixies_and_undi_roms ( void ) {
1104*1b8adde7SWilliam Kucharski 	static uint8_t hunt_type = HUNT_FOR_PIXIES;
1105*1b8adde7SWilliam Kucharski 
1106*1b8adde7SWilliam Kucharski 	if ( hunt_type == HUNT_FOR_PIXIES ) {
1107*1b8adde7SWilliam Kucharski 		if ( hunt_pixie() ) {
1108*1b8adde7SWilliam Kucharski 			return 1;
1109*1b8adde7SWilliam Kucharski 		}
1110*1b8adde7SWilliam Kucharski 	}
1111*1b8adde7SWilliam Kucharski 	hunt_type = HUNT_FOR_UNDI_ROMS;
1112*1b8adde7SWilliam Kucharski 	while ( hunt_undi_rom() ) {
1113*1b8adde7SWilliam Kucharski 		if ( undi_loader() ) {
1114*1b8adde7SWilliam Kucharski 			return 1;
1115*1b8adde7SWilliam Kucharski 		}
1116*1b8adde7SWilliam Kucharski 		undi_full_shutdown(); /* Free any allocated memory */
1117*1b8adde7SWilliam Kucharski 	}
1118*1b8adde7SWilliam Kucharski 	hunt_type = HUNT_FOR_PIXIES;
1119*1b8adde7SWilliam Kucharski 	return 0;
1120*1b8adde7SWilliam Kucharski }
1121*1b8adde7SWilliam Kucharski 
1122*1b8adde7SWilliam Kucharski /* The actual Etherboot probe routine.
1123*1b8adde7SWilliam Kucharski  */
1124*1b8adde7SWilliam Kucharski 
1125*1b8adde7SWilliam Kucharski static int undi_probe(struct dev *dev, struct pci_device *pci)
1126*1b8adde7SWilliam Kucharski {
1127*1b8adde7SWilliam Kucharski 	struct nic *nic = (struct nic *)dev;
1128*1b8adde7SWilliam Kucharski 
1129*1b8adde7SWilliam Kucharski 	/* Zero out global undi structure */
1130*1b8adde7SWilliam Kucharski 	memset ( &undi, 0, sizeof(undi) );
1131*1b8adde7SWilliam Kucharski 
1132*1b8adde7SWilliam Kucharski 	/* Store PCI parameters; we will need them to initialize the UNDI
1133*1b8adde7SWilliam Kucharski 	 * driver later.
1134*1b8adde7SWilliam Kucharski 	 */
1135*1b8adde7SWilliam Kucharski 	memcpy ( &undi.pci, pci, sizeof(undi.pci) );
1136*1b8adde7SWilliam Kucharski 
1137*1b8adde7SWilliam Kucharski 	/* Find the BIOS' $PnP structure */
1138*1b8adde7SWilliam Kucharski 	if ( ! hunt_pnp_bios() ) {
1139*1b8adde7SWilliam Kucharski 		printf ( "No PnP BIOS found; aborting\n" );
1140*1b8adde7SWilliam Kucharski 		return 0;
1141*1b8adde7SWilliam Kucharski 	}
1142*1b8adde7SWilliam Kucharski 
1143*1b8adde7SWilliam Kucharski 	/* Allocate base memory data structures */
1144*1b8adde7SWilliam Kucharski 	if ( ! allocate_base_mem_data() ) return 0;
1145*1b8adde7SWilliam Kucharski 
1146*1b8adde7SWilliam Kucharski 	/* Search thoroughly for UNDI drivers */
1147*1b8adde7SWilliam Kucharski 	for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
1148*1b8adde7SWilliam Kucharski 		/* Try to initialise UNDI driver */
1149*1b8adde7SWilliam Kucharski 		DBG ( "Initializing UNDI driver.  Please wait...\n" );
1150*1b8adde7SWilliam Kucharski 		if ( ! undi_full_startup() ) {
1151*1b8adde7SWilliam Kucharski 			if ( undi.pxs->Status ==
1152*1b8adde7SWilliam Kucharski 			     PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
1153*1b8adde7SWilliam Kucharski 				DBG ( "Cable not connected (code %#hx)\n",
1154*1b8adde7SWilliam Kucharski 					 PXENV_STATUS_UNDI_MEDIATEST_FAILED );
1155*1b8adde7SWilliam Kucharski 			}
1156*1b8adde7SWilliam Kucharski 			continue;
1157*1b8adde7SWilliam Kucharski 		}
1158*1b8adde7SWilliam Kucharski 		/* Basic information: MAC, IO addr, IRQ */
1159*1b8adde7SWilliam Kucharski 		if ( ! eb_pxenv_undi_get_information() ) continue;
1160*1b8adde7SWilliam Kucharski 		DBG ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
1161*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.BaseIo,
1162*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.IntNumber,
1163*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.CurrentNodeAddress );
1164*1b8adde7SWilliam Kucharski 		/* Fill out MAC address in nic structure */
1165*1b8adde7SWilliam Kucharski 		memcpy ( nic->node_addr,
1166*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.CurrentNodeAddress,
1167*1b8adde7SWilliam Kucharski 			 ETH_ALEN );
1168*1b8adde7SWilliam Kucharski 		/* More diagnostic information including link speed */
1169*1b8adde7SWilliam Kucharski 		if ( ! eb_pxenv_undi_get_iface_info() ) continue;
1170*1b8adde7SWilliam Kucharski 		printf ( "  NDIS type %s interface at %d Mbps\n",
1171*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_iface_info.IfaceType,
1172*1b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
1173*1b8adde7SWilliam Kucharski 		DBG ("UNDI Stack at %#hx:%#hx",UNDI_STACK_SEG, UNDI_STACK_OFF);
1174*1b8adde7SWilliam Kucharski 		dev->disable  = undi_disable;
1175*1b8adde7SWilliam Kucharski 		nic->poll     = undi_poll;
1176*1b8adde7SWilliam Kucharski 		nic->transmit = undi_transmit;
1177*1b8adde7SWilliam Kucharski 		return 1;
1178*1b8adde7SWilliam Kucharski 	}
1179*1b8adde7SWilliam Kucharski 	undi_disable ( dev ); /* To free base memory structures */
1180*1b8adde7SWilliam Kucharski 	return 0;
1181*1b8adde7SWilliam Kucharski }
1182*1b8adde7SWilliam Kucharski 
1183*1b8adde7SWilliam Kucharski /* UNDI driver states that it is suitable for any PCI NIC (i.e. any
1184*1b8adde7SWilliam Kucharski  * PCI device of class PCI_CLASS_NETWORK_ETHERNET).  If there are any
1185*1b8adde7SWilliam Kucharski  * obscure UNDI NICs that have the incorrect PCI class, add them to
1186*1b8adde7SWilliam Kucharski  * this list.
1187*1b8adde7SWilliam Kucharski  */
1188*1b8adde7SWilliam Kucharski static struct pci_id undi_nics[] = {
1189*1b8adde7SWilliam Kucharski 	/* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */
1190*1b8adde7SWilliam Kucharski };
1191*1b8adde7SWilliam Kucharski 
1192*1b8adde7SWilliam Kucharski struct pci_driver undi_driver = {
1193*1b8adde7SWilliam Kucharski 	.type     = NIC_DRIVER,
1194*1b8adde7SWilliam Kucharski 	.name     = "UNDI",
1195*1b8adde7SWilliam Kucharski 	.probe    = undi_probe,
1196*1b8adde7SWilliam Kucharski 	.ids      = undi_nics,
1197*1b8adde7SWilliam Kucharski  	.id_count = sizeof(undi_nics)/sizeof(undi_nics[0]),
1198*1b8adde7SWilliam Kucharski 	.class    = PCI_CLASS_NETWORK_ETHERNET,
1199*1b8adde7SWilliam Kucharski };
1200*1b8adde7SWilliam Kucharski 
1201*1b8adde7SWilliam Kucharski /************************************************
1202*1b8adde7SWilliam Kucharski  * Code for reusing the BIOS provided pxe stack
1203*1b8adde7SWilliam Kucharski  */
1204*1b8adde7SWilliam Kucharski 
1205*1b8adde7SWilliam Kucharski /* Verify !PXE structure saved by pxeloader. */
1206*1b8adde7SWilliam Kucharski int undi_bios_pxe(void **dhcpreply)
1207*1b8adde7SWilliam Kucharski {
1208*1b8adde7SWilliam Kucharski 	pxe_t *pxe;
1209*1b8adde7SWilliam Kucharski 	uint16_t *ptr = (uint16_t *)0x7C80;
1210*1b8adde7SWilliam Kucharski 
1211*1b8adde7SWilliam Kucharski 	pxe = (pxe_t *) VIRTUAL(ptr[0], ptr[1]);
1212*1b8adde7SWilliam Kucharski 	if (memcmp(pxe->Signature, "!PXE", 4) != 0) {
1213*1b8adde7SWilliam Kucharski 		DBG ("invalid !PXE signature at %x:%x\n", ptr[0], ptr[1]);
1214*1b8adde7SWilliam Kucharski 		return 0;
1215*1b8adde7SWilliam Kucharski 	}
1216*1b8adde7SWilliam Kucharski 
1217*1b8adde7SWilliam Kucharski 	if (checksum(pxe, sizeof(pxe_t)) != 0) {
1218*1b8adde7SWilliam Kucharski 		DBG ("invalid checksum\n");
1219*1b8adde7SWilliam Kucharski 		return 0;
1220*1b8adde7SWilliam Kucharski 	}
1221*1b8adde7SWilliam Kucharski 
1222*1b8adde7SWilliam Kucharski 	/* Zero out global undi structure */
1223*1b8adde7SWilliam Kucharski 	memset (&undi, 0, sizeof(undi));
1224*1b8adde7SWilliam Kucharski 
1225*1b8adde7SWilliam Kucharski 	/* Allocate base memory data structures */
1226*1b8adde7SWilliam Kucharski 	if (! allocate_base_mem_data()) return 0;
1227*1b8adde7SWilliam Kucharski 
1228*1b8adde7SWilliam Kucharski 	undi.pxe = pxe;
1229*1b8adde7SWilliam Kucharski 	pxe_dump();
1230*1b8adde7SWilliam Kucharski 
1231*1b8adde7SWilliam Kucharski 	if (!eb_pxenv_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, dhcpreply)) {
1232*1b8adde7SWilliam Kucharski 		DBG ("failed to get cached DHCP reply\n");
1233*1b8adde7SWilliam Kucharski 		return 0;
1234*1b8adde7SWilliam Kucharski 	}
1235*1b8adde7SWilliam Kucharski 	return 1;
1236*1b8adde7SWilliam Kucharski }
1237*1b8adde7SWilliam Kucharski 
1238*1b8adde7SWilliam Kucharski void undi_pxe_disable(void)
1239*1b8adde7SWilliam Kucharski {
1240*1b8adde7SWilliam Kucharski 	/* full shutdown is problematic for some machines */
1241*1b8adde7SWilliam Kucharski 	(void) eb_pxenv_undi_shutdown();
1242*1b8adde7SWilliam Kucharski }
1243*1b8adde7SWilliam Kucharski 
1244*1b8adde7SWilliam Kucharski /*
1245*1b8adde7SWilliam Kucharski  * Various helper functions for dhcp and tftp
1246*1b8adde7SWilliam Kucharski  */
1247*1b8adde7SWilliam Kucharski int eb_pxenv_get_cached_info (uint8_t type, void **info)
1248*1b8adde7SWilliam Kucharski {
1249*1b8adde7SWilliam Kucharski 	int success;
1250*1b8adde7SWilliam Kucharski 
1251*1b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
1252*1b8adde7SWilliam Kucharski 	/* Segment:offset pointer to DestAddr in base memory */
1253*1b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.PacketType = type;
1254*1b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.BufferSize = 0;
1255*1b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.Buffer.segment = 0;
1256*1b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.Buffer.offset = 0;
1257*1b8adde7SWilliam Kucharski 
1258*1b8adde7SWilliam Kucharski 	success = undi_call(PXENV_GET_CACHED_INFO);
1259*1b8adde7SWilliam Kucharski 	DBG ("PXENV_GET_CACHED_INFO <= Status=%s\n", UNDI_STATUS(undi.pxs));
1260*1b8adde7SWilliam Kucharski 
1261*1b8adde7SWilliam Kucharski 	*info = (void *)VIRTUAL(undi.pxs->get_cached_info.Buffer.segment,
1262*1b8adde7SWilliam Kucharski 	    undi.pxs->get_cached_info.Buffer.offset);
1263*1b8adde7SWilliam Kucharski 	return success;
1264*1b8adde7SWilliam Kucharski }
1265*1b8adde7SWilliam Kucharski 
1266*1b8adde7SWilliam Kucharski /* tftp help routines */
1267*1b8adde7SWilliam Kucharski int eb_pxenv_tftp_open(char *file, IP4_t serverip, IP4_t gatewayip,
1268*1b8adde7SWilliam Kucharski     uint16_t *pktlen)
1269*1b8adde7SWilliam Kucharski {
1270*1b8adde7SWilliam Kucharski 	int success;
1271*1b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
1272*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.ServerIPAddress = serverip;
1273*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
1274*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.TFTPPort = htons(TFTP_PORT);
1275*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.PacketSize = TFTP_MAX_PACKET;
1276*1b8adde7SWilliam Kucharski 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
1277*1b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_OPEN);
1278*1b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs));
1279*1b8adde7SWilliam Kucharski 	*pktlen = undi.pxs->tftp_open.PacketSize;
1280*1b8adde7SWilliam Kucharski 	return success;
1281*1b8adde7SWilliam Kucharski }
1282*1b8adde7SWilliam Kucharski 
1283*1b8adde7SWilliam Kucharski int eb_pxenv_tftp_read(uint8_t *buf, uint16_t *len)
1284*1b8adde7SWilliam Kucharski {
1285*1b8adde7SWilliam Kucharski 	static int tftp_count = 0;
1286*1b8adde7SWilliam Kucharski 
1287*1b8adde7SWilliam Kucharski 	int success;
1288*1b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
1289*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_read.Buffer.segment = SEGMENT(buf);
1290*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_read.Buffer.offset = OFFSET(buf);
1291*1b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_READ);
1292*1b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_READ <= Status=%s\n", UNDI_STATUS(undi.pxs));
1293*1b8adde7SWilliam Kucharski 	*len = undi.pxs->tftp_read.BufferSize;
1294*1b8adde7SWilliam Kucharski 	tftp_count++;
1295*1b8adde7SWilliam Kucharski 	if ((tftp_count % 1000) == 0)
1296*1b8adde7SWilliam Kucharski 		noisy_printf(".");
1297*1b8adde7SWilliam Kucharski 	return success;
1298*1b8adde7SWilliam Kucharski }
1299*1b8adde7SWilliam Kucharski 
1300*1b8adde7SWilliam Kucharski int eb_pxenv_tftp_close(void)
1301*1b8adde7SWilliam Kucharski {
1302*1b8adde7SWilliam Kucharski 	int success;
1303*1b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
1304*1b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_CLOSE);
1305*1b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs));
1306*1b8adde7SWilliam Kucharski 	return success;
1307*1b8adde7SWilliam Kucharski }
1308*1b8adde7SWilliam Kucharski 
1309*1b8adde7SWilliam Kucharski int eb_pxenv_tftp_get_fsize(char *file, IP4_t serverip, IP4_t gatewayip,
1310*1b8adde7SWilliam Kucharski     uint32_t *fsize)
1311*1b8adde7SWilliam Kucharski {
1312*1b8adde7SWilliam Kucharski 	int success;
1313*1b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
1314*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.ServerIPAddress = serverip;
1315*1b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
1316*1b8adde7SWilliam Kucharski 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
1317*1b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_GET_FSIZE);
1318*1b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_GET_FSIZE <= Status=%s\n", UNDI_STATUS(undi.pxs));
1319*1b8adde7SWilliam Kucharski 	*fsize = undi.pxs->tftp_get_fsize.FileSize;
1320*1b8adde7SWilliam Kucharski 	return success;
1321*1b8adde7SWilliam Kucharski }
1322