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