xref: /titanic_51/usr/src/grub/grub-0.97/netboot/undi.c (revision 6148443adeb5d3f493cee0d19110b32a0189bd41)
11b8adde7SWilliam Kucharski /**************************************************************************
21b8adde7SWilliam Kucharski Etherboot -  BOOTP/TFTP Bootstrap Program
31b8adde7SWilliam Kucharski UNDI NIC driver for Etherboot
41b8adde7SWilliam Kucharski 
51b8adde7SWilliam Kucharski This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
61b8adde7SWilliam Kucharski of Fen Systems Ltd. (http://www.fensystems.co.uk/).  All rights
71b8adde7SWilliam Kucharski reserved.
81b8adde7SWilliam Kucharski 
91b8adde7SWilliam Kucharski $Id: undi.c,v 1.8 2003/10/25 13:54:53 mcb30 Exp $
101b8adde7SWilliam Kucharski ***************************************************************************/
111b8adde7SWilliam Kucharski 
121b8adde7SWilliam Kucharski /*
131b8adde7SWilliam Kucharski  * This program is free software; you can redistribute it and/or
141b8adde7SWilliam Kucharski  * modify it under the terms of the GNU General Public License as
151b8adde7SWilliam Kucharski  * published by the Free Software Foundation; either version 2, or (at
161b8adde7SWilliam Kucharski  * your option) any later version.
171b8adde7SWilliam Kucharski  */
181b8adde7SWilliam Kucharski 
191b8adde7SWilliam Kucharski /* to get some global routines like printf */
201b8adde7SWilliam Kucharski #include "etherboot.h"
211b8adde7SWilliam Kucharski /* to get the interface to the body of the program */
221b8adde7SWilliam Kucharski #include "nic.h"
231b8adde7SWilliam Kucharski /* to get the PCI support functions, if this is a PCI NIC */
241b8adde7SWilliam Kucharski #include "pci.h"
251b8adde7SWilliam Kucharski /* UNDI and PXE defines.  Includes pxe.h. */
261b8adde7SWilliam Kucharski #include "undi.h"
271b8adde7SWilliam Kucharski /* 8259 PIC defines */
281b8adde7SWilliam Kucharski #include "pic8259.h"
291b8adde7SWilliam Kucharski #include "bootp.h"
301b8adde7SWilliam Kucharski #include "tftp.h"
311b8adde7SWilliam Kucharski #include "shared.h"
321b8adde7SWilliam Kucharski 
331b8adde7SWilliam Kucharski /* NIC specific static variables go here */
341b8adde7SWilliam Kucharski static undi_t undi = { NULL, NULL, NULL, NULL, NULL, NULL, NULL,
351b8adde7SWilliam Kucharski 		       NULL, NULL, 0, NULL, 0, NULL,
361b8adde7SWilliam Kucharski 		       0, 0, 0, 0,
371b8adde7SWilliam Kucharski 		       { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL },
381b8adde7SWilliam Kucharski 		       IRQ_NONE };
391b8adde7SWilliam Kucharski static undi_base_mem_data_t undi_base_mem_data;
401b8adde7SWilliam Kucharski 
411b8adde7SWilliam Kucharski #define UNDI_HEAP (void *)(512 << 10)
421b8adde7SWilliam Kucharski 
431b8adde7SWilliam Kucharski /* Function prototypes */
441b8adde7SWilliam Kucharski int allocate_base_mem_data ( void );
451b8adde7SWilliam Kucharski int free_base_mem_data ( void );
461b8adde7SWilliam Kucharski int eb_pxenv_undi_shutdown ( void );
471b8adde7SWilliam Kucharski int eb_pxenv_stop_undi ( void );
481b8adde7SWilliam Kucharski int undi_unload_base_code ( void );
491b8adde7SWilliam Kucharski int undi_full_shutdown ( void );
501b8adde7SWilliam Kucharski int eb_pxenv_get_cached_info (uint8_t, void **info);
511b8adde7SWilliam Kucharski 
521b8adde7SWilliam Kucharski /**************************************************************************
531b8adde7SWilliam Kucharski  * Utility functions
541b8adde7SWilliam Kucharski  **************************************************************************/
551b8adde7SWilliam Kucharski 
561b8adde7SWilliam Kucharski /* Checksum a block.
571b8adde7SWilliam Kucharski  */
581b8adde7SWilliam Kucharski 
591b8adde7SWilliam Kucharski uint8_t checksum ( void *block, size_t size ) {
601b8adde7SWilliam Kucharski 	uint8_t sum = 0;
611b8adde7SWilliam Kucharski 	uint16_t i = 0;
621b8adde7SWilliam Kucharski 	for ( i = 0; i < size; i++ ) {
631b8adde7SWilliam Kucharski 		sum += ( ( uint8_t * ) block )[i];
641b8adde7SWilliam Kucharski 	}
651b8adde7SWilliam Kucharski 	return sum;
661b8adde7SWilliam Kucharski }
671b8adde7SWilliam Kucharski 
681b8adde7SWilliam Kucharski /* Print the status of a !PXE structure
691b8adde7SWilliam Kucharski  */
701b8adde7SWilliam Kucharski 
711b8adde7SWilliam Kucharski void pxe_dump ( void ) {
721b8adde7SWilliam Kucharski #ifdef TRACE_UNDI
731b8adde7SWilliam Kucharski 	printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
741b8adde7SWilliam Kucharski 		 "BD %hx:%hx BC %hx:%hx\n",
751b8adde7SWilliam Kucharski 		 undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset,
761b8adde7SWilliam Kucharski 		 undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size,
771b8adde7SWilliam Kucharski 		 undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size,
781b8adde7SWilliam Kucharski 		 undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size,
791b8adde7SWilliam Kucharski 		 undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size,
801b8adde7SWilliam Kucharski 		 undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size );
811b8adde7SWilliam Kucharski #endif
821b8adde7SWilliam Kucharski }
831b8adde7SWilliam Kucharski 
841b8adde7SWilliam Kucharski /* Allocate/free space for structures that must reside in base memory
851b8adde7SWilliam Kucharski  */
861b8adde7SWilliam Kucharski 
871b8adde7SWilliam Kucharski int allocate_base_mem_data ( void ) {
881b8adde7SWilliam Kucharski 	/* In GRUB, anything is in base address, so we do not need
891b8adde7SWilliam Kucharski 	 * allocate anything */
901b8adde7SWilliam Kucharski 	undi.base_mem_data = &undi_base_mem_data;
911b8adde7SWilliam Kucharski 	memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
921b8adde7SWilliam Kucharski 	undi.undi_call_info = &undi.base_mem_data->undi_call_info;
931b8adde7SWilliam Kucharski 	undi.pxs = &undi.base_mem_data->pxs;
941b8adde7SWilliam Kucharski 	undi.xmit_data = &undi.base_mem_data->xmit_data;
951b8adde7SWilliam Kucharski 	undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
961b8adde7SWilliam Kucharski #if 0				/* Etherboot Code */
971b8adde7SWilliam Kucharski 	/* Allocate space in base memory.
981b8adde7SWilliam Kucharski 	 * Initialise pointers to base memory structures.
991b8adde7SWilliam Kucharski 	 */
1001b8adde7SWilliam Kucharski 	if ( undi.base_mem_data == NULL ) {
1011b8adde7SWilliam Kucharski 		undi.base_mem_data =
1021b8adde7SWilliam Kucharski 			allot_base_memory ( sizeof(undi_base_mem_data_t) +
1031b8adde7SWilliam Kucharski 					    TRIVIAL_IRQ_HANDLER_SIZE );
1041b8adde7SWilliam Kucharski 		if ( undi.base_mem_data == NULL ) {
1051b8adde7SWilliam Kucharski 			printf ( "Failed to allocate base memory\n" );
1061b8adde7SWilliam Kucharski 			free_base_mem_data();
1071b8adde7SWilliam Kucharski 			return 0;
1081b8adde7SWilliam Kucharski 		}
1091b8adde7SWilliam Kucharski 		memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
1101b8adde7SWilliam Kucharski 		undi.undi_call_info = &undi.base_mem_data->undi_call_info;
1111b8adde7SWilliam Kucharski 		undi.pxs = &undi.base_mem_data->pxs;
1121b8adde7SWilliam Kucharski 		undi.xmit_data = &undi.base_mem_data->xmit_data;
1131b8adde7SWilliam Kucharski 		undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
1141b8adde7SWilliam Kucharski 		copy_trivial_irq_handler ( undi.base_mem_data->irq_handler,
1151b8adde7SWilliam Kucharski 					   TRIVIAL_IRQ_HANDLER_SIZE );
1161b8adde7SWilliam Kucharski 	}
1171b8adde7SWilliam Kucharski #endif	/* Etherboot Code */
1181b8adde7SWilliam Kucharski 	return 1;
1191b8adde7SWilliam Kucharski }
1201b8adde7SWilliam Kucharski 
1211b8adde7SWilliam Kucharski int free_base_mem_data ( void ) {
1221b8adde7SWilliam Kucharski 	/* Just pretend to free something :-) */
1231b8adde7SWilliam Kucharski 	undi.base_mem_data = NULL;
1241b8adde7SWilliam Kucharski 	undi.undi_call_info = NULL;
1251b8adde7SWilliam Kucharski 	undi.pxs = NULL;
1261b8adde7SWilliam Kucharski 	undi.xmit_data = NULL;
1271b8adde7SWilliam Kucharski 	undi.xmit_buffer = NULL;
1281b8adde7SWilliam Kucharski #if 0				/* Etherboot Code */
1291b8adde7SWilliam Kucharski 	if ( undi.base_mem_data != NULL ) {
1301b8adde7SWilliam Kucharski 		forget_base_memory ( undi.base_mem_data,
1311b8adde7SWilliam Kucharski 				     sizeof(undi_base_mem_data_t) +
1321b8adde7SWilliam Kucharski 				     TRIVIAL_IRQ_HANDLER_SIZE );
1331b8adde7SWilliam Kucharski 		undi.base_mem_data = NULL;
1341b8adde7SWilliam Kucharski 		undi.undi_call_info = NULL;
1351b8adde7SWilliam Kucharski 		undi.pxs = NULL;
1361b8adde7SWilliam Kucharski 		undi.xmit_data = NULL;
1371b8adde7SWilliam Kucharski 		undi.xmit_buffer = NULL;
1381b8adde7SWilliam Kucharski 		copy_trivial_irq_handler ( NULL, 0 );
1391b8adde7SWilliam Kucharski 	}
1401b8adde7SWilliam Kucharski #endif	/* Etherboot Code */
1411b8adde7SWilliam Kucharski 	return 1;
1421b8adde7SWilliam Kucharski }
1431b8adde7SWilliam Kucharski 
1441b8adde7SWilliam Kucharski void assemble_firing_squad ( firing_squad_lineup_t *lineup,
1451b8adde7SWilliam Kucharski 			     void *start, size_t size,
1461b8adde7SWilliam Kucharski 			     firing_squad_shoot_t shoot ) {
1471b8adde7SWilliam Kucharski 	int target;
1481b8adde7SWilliam Kucharski 	int index;
1491b8adde7SWilliam Kucharski 	int bit;
1501b8adde7SWilliam Kucharski 	int start_kb = virt_to_phys(start) >> 10;
1511b8adde7SWilliam Kucharski 	int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
1521b8adde7SWilliam Kucharski 
1531b8adde7SWilliam Kucharski 	for ( target = start_kb; target <= end_kb; target++ ) {
1541b8adde7SWilliam Kucharski 		index = FIRING_SQUAD_TARGET_INDEX ( target );
1551b8adde7SWilliam Kucharski 		bit = FIRING_SQUAD_TARGET_BIT ( target );
1561b8adde7SWilliam Kucharski 		lineup->targets[index] = ( shoot << bit ) |
1571b8adde7SWilliam Kucharski 			( lineup->targets[index] & ~( 1 << bit ) );
1581b8adde7SWilliam Kucharski 	}
1591b8adde7SWilliam Kucharski }
1601b8adde7SWilliam Kucharski 
1611b8adde7SWilliam Kucharski void shoot_targets ( firing_squad_lineup_t *lineup ) {
1621b8adde7SWilliam Kucharski 	int shoot_this_target = 0;
1631b8adde7SWilliam Kucharski 	int shoot_last_target = 0;
1641b8adde7SWilliam Kucharski 	int start_target = 0;
1651b8adde7SWilliam Kucharski 	int target;
1661b8adde7SWilliam Kucharski 
1671b8adde7SWilliam Kucharski 	for ( target = 0; target <= 640; target++ ) {
1681b8adde7SWilliam Kucharski 		shoot_this_target = ( target == 640 ? 0 :
1691b8adde7SWilliam Kucharski 		      ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) &
1701b8adde7SWilliam Kucharski 		      lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] );
1711b8adde7SWilliam Kucharski 		if ( shoot_this_target && !shoot_last_target ) {
1721b8adde7SWilliam Kucharski 			start_target = target;
1731b8adde7SWilliam Kucharski 		} else if ( shoot_last_target && !shoot_this_target ) {
1741b8adde7SWilliam Kucharski 			size_t range_size = ( target - start_target ) << 10;
1751b8adde7SWilliam Kucharski 			forget_base_memory ( phys_to_virt( start_target<<10 ),
1761b8adde7SWilliam Kucharski 					     range_size );
1771b8adde7SWilliam Kucharski 		}
1781b8adde7SWilliam Kucharski 		shoot_last_target = shoot_this_target;
1791b8adde7SWilliam Kucharski 	}
1801b8adde7SWilliam Kucharski }
1811b8adde7SWilliam Kucharski 
1821b8adde7SWilliam Kucharski /* Debug macros
1831b8adde7SWilliam Kucharski  */
1841b8adde7SWilliam Kucharski 
1851b8adde7SWilliam Kucharski #ifdef TRACE_UNDI
1861b8adde7SWilliam Kucharski #define DBG(...) printf ( __VA_ARGS__ )
1871b8adde7SWilliam Kucharski #else
1881b8adde7SWilliam Kucharski #define DBG(...)
1891b8adde7SWilliam Kucharski #endif
1901b8adde7SWilliam Kucharski 
1911b8adde7SWilliam Kucharski #define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
1921b8adde7SWilliam Kucharski 			      "SUCCESS" : \
1931b8adde7SWilliam Kucharski 			      ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
1941b8adde7SWilliam Kucharski 				"FAILURE" : "UNKNOWN" ) )
1951b8adde7SWilliam Kucharski 
1961b8adde7SWilliam Kucharski /**************************************************************************
1971b8adde7SWilliam Kucharski  * Base memory scanning functions
1981b8adde7SWilliam Kucharski  **************************************************************************/
1991b8adde7SWilliam Kucharski 
2001b8adde7SWilliam Kucharski /* Locate the $PnP structure indicating a PnP BIOS.
2011b8adde7SWilliam Kucharski  */
2021b8adde7SWilliam Kucharski 
2031b8adde7SWilliam Kucharski int hunt_pnp_bios ( void ) {
2041b8adde7SWilliam Kucharski 	uint32_t off = 0x10000;
2051b8adde7SWilliam Kucharski 
2061b8adde7SWilliam Kucharski 	DBG ( "Hunting for PnP BIOS..." );
2071b8adde7SWilliam Kucharski 	while ( off > 0 ) {
2081b8adde7SWilliam Kucharski 		off -= 16;
2091b8adde7SWilliam Kucharski 		undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off );
2101b8adde7SWilliam Kucharski 		if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) {
2111b8adde7SWilliam Kucharski 			DBG ( "found $PnP at f000:%hx...", off );
2121b8adde7SWilliam Kucharski 			if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) {
2131b8adde7SWilliam Kucharski 				DBG ( "invalid checksum\n..." );
2141b8adde7SWilliam Kucharski 				continue;
2151b8adde7SWilliam Kucharski 			}
2161b8adde7SWilliam Kucharski 			DBG ( "ok\n" );
2171b8adde7SWilliam Kucharski 			return 1;
2181b8adde7SWilliam Kucharski 		}
2191b8adde7SWilliam Kucharski 	}
2201b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
2211b8adde7SWilliam Kucharski 	undi.pnp_bios = NULL;
2221b8adde7SWilliam Kucharski 	return 0;
2231b8adde7SWilliam Kucharski }
2241b8adde7SWilliam Kucharski 
2251b8adde7SWilliam Kucharski /* Locate the !PXE structure indicating a loaded UNDI driver.
2261b8adde7SWilliam Kucharski  */
2271b8adde7SWilliam Kucharski 
2281b8adde7SWilliam Kucharski int hunt_pixie ( void ) {
2291b8adde7SWilliam Kucharski 	static uint32_t ptr = 0;
2301b8adde7SWilliam Kucharski 	pxe_t *pxe = NULL;
2311b8adde7SWilliam Kucharski 
2321b8adde7SWilliam Kucharski 	DBG ( "Hunting for pixies..." );
2331b8adde7SWilliam Kucharski 	if ( ptr == 0 ) ptr = 0xa0000;
2341b8adde7SWilliam Kucharski 	while ( ptr > 0x10000 ) {
2351b8adde7SWilliam Kucharski 		ptr -= 16;
2361b8adde7SWilliam Kucharski 		pxe = (pxe_t *) phys_to_virt ( ptr );
2371b8adde7SWilliam Kucharski 		if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) {
2381b8adde7SWilliam Kucharski 			DBG ( "found !PXE at %x...", ptr );
2391b8adde7SWilliam Kucharski 			if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
2401b8adde7SWilliam Kucharski 				DBG ( "invalid checksum\n..." );
2411b8adde7SWilliam Kucharski 				continue;
2421b8adde7SWilliam Kucharski 			}
2431b8adde7SWilliam Kucharski 			if ( ptr < get_free_base_memory() ) {
2441b8adde7SWilliam Kucharski 				DBG ( "in free base memory!\n\n"
2451b8adde7SWilliam Kucharski 					 "WARNING: a valid !PXE structure was "
2461b8adde7SWilliam Kucharski 					 "found in an area of memory marked "
2471b8adde7SWilliam Kucharski 					 "as free!\n\n" );
2481b8adde7SWilliam Kucharski 				undi.pxe = pxe;
2491b8adde7SWilliam Kucharski 				pxe_dump();
2501b8adde7SWilliam Kucharski 				undi.pxe = NULL;
2511b8adde7SWilliam Kucharski 				DBG ( "\nIgnoring and continuing, but this "
2521b8adde7SWilliam Kucharski 					 "may cause problems later!\n\n" );
2531b8adde7SWilliam Kucharski 				continue;
2541b8adde7SWilliam Kucharski 			}
2551b8adde7SWilliam Kucharski 			DBG ( "ok\n" );
2561b8adde7SWilliam Kucharski 			undi.pxe = pxe;
2571b8adde7SWilliam Kucharski 			pxe_dump();
2581b8adde7SWilliam Kucharski 			DBG ( "Resetting pixie...\n" );
2591b8adde7SWilliam Kucharski 			undi_unload_base_code();
2601b8adde7SWilliam Kucharski 			eb_pxenv_stop_undi();
2611b8adde7SWilliam Kucharski 			pxe_dump();
2621b8adde7SWilliam Kucharski 			return 1;
2631b8adde7SWilliam Kucharski 		}
2641b8adde7SWilliam Kucharski 	}
2651b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
2661b8adde7SWilliam Kucharski 	ptr = 0;
2671b8adde7SWilliam Kucharski 	return 0;
2681b8adde7SWilliam Kucharski }
2691b8adde7SWilliam Kucharski 
2701b8adde7SWilliam Kucharski /* Locate PCI PnP ROMs.
2711b8adde7SWilliam Kucharski  */
2721b8adde7SWilliam Kucharski 
2731b8adde7SWilliam Kucharski int hunt_rom ( void ) {
2741b8adde7SWilliam Kucharski 	static uint32_t ptr = 0;
2751b8adde7SWilliam Kucharski 
2761b8adde7SWilliam Kucharski 	DBG ( "Hunting for ROMs..." );
2771b8adde7SWilliam Kucharski 	if ( ptr == 0 ) ptr = 0x100000;
2781b8adde7SWilliam Kucharski 	while ( ptr > 0x0c0000 ) {
2791b8adde7SWilliam Kucharski 		ptr -= 0x800;
2801b8adde7SWilliam Kucharski 		undi.rom = ( rom_t * ) phys_to_virt ( ptr );
2811b8adde7SWilliam Kucharski 		if ( undi.rom->signature == ROM_SIGNATURE ) {
2821b8adde7SWilliam Kucharski 			pcir_header_t *pcir_header = NULL;
2831b8adde7SWilliam Kucharski 			pnp_header_t *pnp_header = NULL;
2841b8adde7SWilliam Kucharski 
2851b8adde7SWilliam Kucharski 			DBG ( "found 55AA at %x...", ptr );
2861b8adde7SWilliam Kucharski 			if ( undi.rom->pcir_off == 0 ) {
2871b8adde7SWilliam Kucharski 				DBG ( "not a PCI ROM\n..." );
2881b8adde7SWilliam Kucharski 				continue;
2891b8adde7SWilliam Kucharski 			}
2901b8adde7SWilliam Kucharski 			pcir_header = (pcir_header_t*)( ( void * ) undi.rom +
2911b8adde7SWilliam Kucharski 							undi.rom->pcir_off );
2921b8adde7SWilliam Kucharski 			if ( pcir_header->signature != PCIR_SIGNATURE ) {
2931b8adde7SWilliam Kucharski 				DBG ( "invalid PCI signature\n..." );
2941b8adde7SWilliam Kucharski 				continue;
2951b8adde7SWilliam Kucharski 			}
2961b8adde7SWilliam Kucharski 			DBG ( "PCI:%hx:%hx...", pcir_header->vendor_id,
2971b8adde7SWilliam Kucharski 				 pcir_header->device_id );
2981b8adde7SWilliam Kucharski 			if ( ( pcir_header->vendor_id != undi.pci.vendor ) ||
2991b8adde7SWilliam Kucharski 			     ( pcir_header->device_id != undi.pci.dev_id ) ) {
3001b8adde7SWilliam Kucharski 				DBG ( "not me (%hx:%hx)\n...",
3011b8adde7SWilliam Kucharski 					 undi.pci.vendor,
3021b8adde7SWilliam Kucharski 					 undi.pci.dev_id );
3031b8adde7SWilliam Kucharski 				continue;
3041b8adde7SWilliam Kucharski 			}
3051b8adde7SWilliam Kucharski 			if ( undi.rom->pnp_off == 0 ) {
3061b8adde7SWilliam Kucharski 				DBG ( "not a PnP ROM\n..." );
3071b8adde7SWilliam Kucharski 				continue;
3081b8adde7SWilliam Kucharski 			}
3091b8adde7SWilliam Kucharski 			pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
3101b8adde7SWilliam Kucharski 							 undi.rom->pnp_off );
3111b8adde7SWilliam Kucharski 			if ( pnp_header->signature != PNP_SIGNATURE ) {
3121b8adde7SWilliam Kucharski 				DBG ( "invalid $PnP signature\n..." );
3131b8adde7SWilliam Kucharski 				continue;
3141b8adde7SWilliam Kucharski 			}
3151b8adde7SWilliam Kucharski 			if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
3161b8adde7SWilliam Kucharski 				DBG ( "invalid PnP checksum\n..." );
3171b8adde7SWilliam Kucharski 				continue;
3181b8adde7SWilliam Kucharski 			}
3191b8adde7SWilliam Kucharski 			DBG ( "ok\n");
3201b8adde7SWilliam Kucharski 			printf ("ROM %s by %s\n",
3211b8adde7SWilliam Kucharski 				 pnp_header->product_str_off==0 ? "(unknown)" :
3221b8adde7SWilliam Kucharski 				 (void*)undi.rom+pnp_header->product_str_off,
3231b8adde7SWilliam Kucharski 				 pnp_header->manuf_str_off==0 ? "(unknown)" :
3241b8adde7SWilliam Kucharski 				 (void*)undi.rom+pnp_header->manuf_str_off );
3251b8adde7SWilliam Kucharski 			return 1;
3261b8adde7SWilliam Kucharski 		}
3271b8adde7SWilliam Kucharski 	}
3281b8adde7SWilliam Kucharski 	DBG ( "none found\n" );
3291b8adde7SWilliam Kucharski 	ptr = 0;
3301b8adde7SWilliam Kucharski 	undi.rom = NULL;
3311b8adde7SWilliam Kucharski 	return 0;
3321b8adde7SWilliam Kucharski }
3331b8adde7SWilliam Kucharski 
3341b8adde7SWilliam Kucharski /* Locate ROMs containing UNDI drivers.
3351b8adde7SWilliam Kucharski  */
3361b8adde7SWilliam Kucharski 
3371b8adde7SWilliam Kucharski int hunt_undi_rom ( void ) {
3381b8adde7SWilliam Kucharski 	while ( hunt_rom() ) {
3391b8adde7SWilliam Kucharski 		if ( undi.rom->undi_rom_id_off == 0 ) {
3401b8adde7SWilliam Kucharski 			DBG ( "Not a PXE ROM\n" );
3411b8adde7SWilliam Kucharski 			continue;
3421b8adde7SWilliam Kucharski 		}
3431b8adde7SWilliam Kucharski 		undi.undi_rom_id = (undi_rom_id_t *)
3441b8adde7SWilliam Kucharski 			( (void *)undi.rom + undi.rom->undi_rom_id_off );
3451b8adde7SWilliam Kucharski 		if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) {
3461b8adde7SWilliam Kucharski 			DBG ( "Invalid UNDI signature\n" );
3471b8adde7SWilliam Kucharski 			continue;
3481b8adde7SWilliam Kucharski 		}
3491b8adde7SWilliam Kucharski 		printf ( "Revision %d.%d.%d",
3501b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[2],
3511b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[1],
3521b8adde7SWilliam Kucharski 			 undi.undi_rom_id->undi_rev[0] );
3531b8adde7SWilliam Kucharski 		return 1;
3541b8adde7SWilliam Kucharski 	}
3551b8adde7SWilliam Kucharski 	return 0;
3561b8adde7SWilliam Kucharski }
3571b8adde7SWilliam Kucharski 
3581b8adde7SWilliam Kucharski /**************************************************************************
3591b8adde7SWilliam Kucharski  * Low-level UNDI API call wrappers
3601b8adde7SWilliam Kucharski  **************************************************************************/
3611b8adde7SWilliam Kucharski 
3621b8adde7SWilliam Kucharski /* Make a real-mode UNDI API call to the UNDI routine at
3631b8adde7SWilliam Kucharski  * routine_seg:routine_off, passing in three uint16 parameters on the
3641b8adde7SWilliam Kucharski  * real-mode stack.
3651b8adde7SWilliam Kucharski  * Calls the assembler wrapper routine __undi_call.
3661b8adde7SWilliam Kucharski  */
3671b8adde7SWilliam Kucharski 
3681b8adde7SWilliam Kucharski static inline PXENV_EXIT_t _undi_call ( uint16_t routine_seg,
3691b8adde7SWilliam Kucharski 					uint16_t routine_off, uint16_t st0,
3701b8adde7SWilliam Kucharski 					uint16_t st1, uint16_t st2 ) {
3711b8adde7SWilliam Kucharski 	PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
3721b8adde7SWilliam Kucharski 
3731b8adde7SWilliam Kucharski 	undi.undi_call_info->routine.segment = routine_seg;
3741b8adde7SWilliam Kucharski 	undi.undi_call_info->routine.offset = routine_off;
3751b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[0] = st0;
3761b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[1] = st1;
3771b8adde7SWilliam Kucharski 	undi.undi_call_info->stack[2] = st2;
3781b8adde7SWilliam Kucharski 	ret = __undi_call ( SEGMENT( undi.undi_call_info ),
3791b8adde7SWilliam Kucharski 			    OFFSET( undi.undi_call_info ) );
3801b8adde7SWilliam Kucharski 
3811b8adde7SWilliam Kucharski 	/* UNDI API calls may rudely change the status of A20 and not
3821b8adde7SWilliam Kucharski 	 * bother to restore it afterwards.  Intel is known to be
3831b8adde7SWilliam Kucharski 	 * guilty of this.
3841b8adde7SWilliam Kucharski 	 *
3851b8adde7SWilliam Kucharski 	 * Note that we will return to this point even if A20 gets
3861b8adde7SWilliam Kucharski 	 * screwed up by the UNDI driver, because Etherboot always
3871b8adde7SWilliam Kucharski 	 * resides in an even megabyte of RAM.
3881b8adde7SWilliam Kucharski 	 */
3891b8adde7SWilliam Kucharski 	gateA20_set();
3901b8adde7SWilliam Kucharski 
3911b8adde7SWilliam Kucharski 	return ret;
3921b8adde7SWilliam Kucharski }
3931b8adde7SWilliam Kucharski 
3941b8adde7SWilliam Kucharski /* Make a real-mode call to the UNDI loader routine at
3951b8adde7SWilliam Kucharski  * routine_seg:routine_off, passing in the seg:off address of a
3961b8adde7SWilliam Kucharski  * pxenv_structure on the real-mode stack.
3971b8adde7SWilliam Kucharski  */
3981b8adde7SWilliam Kucharski 
3991b8adde7SWilliam Kucharski int undi_call_loader ( void ) {
4001b8adde7SWilliam Kucharski 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
4011b8adde7SWilliam Kucharski 
4021b8adde7SWilliam Kucharski 	pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
4031b8adde7SWilliam Kucharski 				  undi.undi_rom_id->undi_loader_off,
4041b8adde7SWilliam Kucharski 				  OFFSET( undi.pxs ),
4051b8adde7SWilliam Kucharski 				  SEGMENT( undi.pxs ),
4061b8adde7SWilliam Kucharski 				  0 /* Unused for UNDI loader API */ );
4071b8adde7SWilliam Kucharski 	/* Return 1 for success, to be consistent with other routines */
4081b8adde7SWilliam Kucharski 	if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1;
4091b8adde7SWilliam Kucharski 	DBG ( "UNDI loader call failed with status %#hx\n",
4101b8adde7SWilliam Kucharski 		 undi.pxs->Status );
4111b8adde7SWilliam Kucharski 	return 0;
4121b8adde7SWilliam Kucharski }
4131b8adde7SWilliam Kucharski 
4141b8adde7SWilliam Kucharski /* Make a real-mode UNDI API call, passing in the opcode and the
4151b8adde7SWilliam Kucharski  * seg:off address of a pxenv_structure on the real-mode stack.
4161b8adde7SWilliam Kucharski  *
4171b8adde7SWilliam Kucharski  * Two versions: undi_call() will automatically report any failure
4181b8adde7SWilliam Kucharski  * codes, undi_call_silent() will not.
4191b8adde7SWilliam Kucharski  */
4201b8adde7SWilliam Kucharski 
4211b8adde7SWilliam Kucharski int undi_call_silent ( uint16_t opcode ) {
4221b8adde7SWilliam Kucharski 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
4231b8adde7SWilliam Kucharski 
4241b8adde7SWilliam Kucharski 	pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
4251b8adde7SWilliam Kucharski 				  undi.pxe->EntryPointSP.offset,
4261b8adde7SWilliam Kucharski 				  opcode,
4271b8adde7SWilliam Kucharski 				  OFFSET( undi.pxs ),
4281b8adde7SWilliam Kucharski 				  SEGMENT( undi.pxs ) );
4291b8adde7SWilliam Kucharski 	/* Return 1 for success, to be consistent with other routines */
4301b8adde7SWilliam Kucharski 	return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
4311b8adde7SWilliam Kucharski }
4321b8adde7SWilliam Kucharski 
4331b8adde7SWilliam Kucharski int undi_call ( uint16_t opcode ) {
4341b8adde7SWilliam Kucharski 	if ( undi_call_silent ( opcode ) ) return 1;
4351b8adde7SWilliam Kucharski 	DBG ( "UNDI API call %#hx failed with status %#hx\n",
4361b8adde7SWilliam Kucharski 		 opcode, undi.pxs->Status );
4371b8adde7SWilliam Kucharski 	return 0;
4381b8adde7SWilliam Kucharski }
4391b8adde7SWilliam Kucharski 
4401b8adde7SWilliam Kucharski /**************************************************************************
4411b8adde7SWilliam Kucharski  * High-level UNDI API call wrappers
4421b8adde7SWilliam Kucharski  **************************************************************************/
4431b8adde7SWilliam Kucharski 
4441b8adde7SWilliam Kucharski /* Install the UNDI driver from a located UNDI ROM.
4451b8adde7SWilliam Kucharski  */
4461b8adde7SWilliam Kucharski 
4471b8adde7SWilliam Kucharski int undi_loader ( void ) {
4481b8adde7SWilliam Kucharski 	pxe_t *pxe = NULL;
4491b8adde7SWilliam Kucharski 
4501b8adde7SWilliam Kucharski 	/* AX contains PCI bus:devfn (PCI specification) */
4511b8adde7SWilliam Kucharski 	undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
4521b8adde7SWilliam Kucharski 	/* BX and DX set to 0xffff for non-ISAPnP devices
4531b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
4541b8adde7SWilliam Kucharski 	 */
4551b8adde7SWilliam Kucharski 	undi.pxs->loader.bx = 0xffff;
4561b8adde7SWilliam Kucharski 	undi.pxs->loader.dx = 0xffff;
4571b8adde7SWilliam Kucharski 	/* ES:DI points to PnP BIOS' $PnP structure
4581b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
4591b8adde7SWilliam Kucharski 	 */
4601b8adde7SWilliam Kucharski 	undi.pxs->loader.es = 0xf000;
4611b8adde7SWilliam Kucharski 	undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
4621b8adde7SWilliam Kucharski 
4631b8adde7SWilliam Kucharski 	/* Allocate space for UNDI driver's code and data segments */
4641b8adde7SWilliam Kucharski 	undi.driver_code_size = undi.undi_rom_id->code_size;
4651b8adde7SWilliam Kucharski 	undi.driver_code = UNDI_HEAP;
4661b8adde7SWilliam Kucharski 	if ( undi.driver_code == NULL ) {
4671b8adde7SWilliam Kucharski 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
4681b8adde7SWilliam Kucharski 			 undi.driver_code_size );
4691b8adde7SWilliam Kucharski 		return 0;
4701b8adde7SWilliam Kucharski 	}
4711b8adde7SWilliam Kucharski 	undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
4721b8adde7SWilliam Kucharski 
4731b8adde7SWilliam Kucharski 	undi.driver_data_size = undi.undi_rom_id->data_size;
4741b8adde7SWilliam Kucharski 	undi.driver_data = (void *)((((unsigned long)UNDI_HEAP + undi.undi_rom_id->code_size) | (1024 -1)) + 1);
4751b8adde7SWilliam Kucharski 	if ( undi.driver_data == NULL ) {
4761b8adde7SWilliam Kucharski 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
4771b8adde7SWilliam Kucharski 			 undi.driver_data_size );
4781b8adde7SWilliam Kucharski 		return 0;
4791b8adde7SWilliam Kucharski 	}
4801b8adde7SWilliam Kucharski 	undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
4811b8adde7SWilliam Kucharski 
4821b8adde7SWilliam Kucharski 	DBG ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
4831b8adde7SWilliam Kucharski 		undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds );
4841b8adde7SWilliam Kucharski 
4851b8adde7SWilliam Kucharski 	/* Do the API call to install the loader */
4861b8adde7SWilliam Kucharski 	if ( ! undi_call_loader () ) return 0;
4871b8adde7SWilliam Kucharski 
4881b8adde7SWilliam Kucharski 	pxe = VIRTUAL( undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
4891b8adde7SWilliam Kucharski 	DBG ( "UNDI driver created a pixie at %hx:%hx...",
4901b8adde7SWilliam Kucharski 		 undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
4911b8adde7SWilliam Kucharski 	if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) {
4921b8adde7SWilliam Kucharski 		DBG ( "invalid signature\n" );
4931b8adde7SWilliam Kucharski 		return 0;
4941b8adde7SWilliam Kucharski 	}
4951b8adde7SWilliam Kucharski 	if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
4961b8adde7SWilliam Kucharski 		DBG ( "invalid checksum\n" );
4971b8adde7SWilliam Kucharski 		return 0;
4981b8adde7SWilliam Kucharski 	}
4991b8adde7SWilliam Kucharski 	DBG ( "ok\n" );
5001b8adde7SWilliam Kucharski 	undi.pxe = pxe;
5011b8adde7SWilliam Kucharski 	pxe_dump();
5021b8adde7SWilliam Kucharski 	return 1;
5031b8adde7SWilliam Kucharski }
5041b8adde7SWilliam Kucharski 
5051b8adde7SWilliam Kucharski /* Start the UNDI driver.
5061b8adde7SWilliam Kucharski  */
5071b8adde7SWilliam Kucharski 
5081b8adde7SWilliam Kucharski int eb_pxenv_start_undi ( void ) {
5091b8adde7SWilliam Kucharski 	int success = 0;
5101b8adde7SWilliam Kucharski 
5111b8adde7SWilliam Kucharski 	/* AX contains PCI bus:devfn (PCI specification) */
5121b8adde7SWilliam Kucharski 	undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
5131b8adde7SWilliam Kucharski 	/* BX and DX set to 0xffff for non-ISAPnP devices
5141b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
5151b8adde7SWilliam Kucharski 	 */
5161b8adde7SWilliam Kucharski 	undi.pxs->start_undi.bx = 0xffff;
5171b8adde7SWilliam Kucharski 	undi.pxs->start_undi.dx = 0xffff;
5181b8adde7SWilliam Kucharski 	/* ES:DI points to PnP BIOS' $PnP structure
5191b8adde7SWilliam Kucharski 	 * (BIOS boot specification)
5201b8adde7SWilliam Kucharski 	 */
5211b8adde7SWilliam Kucharski 	undi.pxs->start_undi.es = 0xf000;
5221b8adde7SWilliam Kucharski 	undi.pxs->start_undi.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
5231b8adde7SWilliam Kucharski 
5241b8adde7SWilliam Kucharski 	DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
5251b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.ax,
5261b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.bx, undi.pxs->start_undi.dx,
5271b8adde7SWilliam Kucharski 	      undi.pxs->start_undi.es, undi.pxs->start_undi.di );
5281b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_START_UNDI );
5291b8adde7SWilliam Kucharski 	DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5301b8adde7SWilliam Kucharski 	if ( success ) undi.prestarted = 1;
5311b8adde7SWilliam Kucharski 	return success;
5321b8adde7SWilliam Kucharski }
5331b8adde7SWilliam Kucharski 
5341b8adde7SWilliam Kucharski int eb_pxenv_undi_startup ( void )	{
5351b8adde7SWilliam Kucharski 	int success = 0;
5361b8adde7SWilliam Kucharski 
5371b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
5381b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_STARTUP );
5391b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5401b8adde7SWilliam Kucharski 	if ( success ) undi.started = 1;
5411b8adde7SWilliam Kucharski 	return success;
5421b8adde7SWilliam Kucharski }
5431b8adde7SWilliam Kucharski 
5441b8adde7SWilliam Kucharski int eb_pxenv_undi_cleanup ( void ) {
5451b8adde7SWilliam Kucharski 	int success = 0;
5461b8adde7SWilliam Kucharski 
5471b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
5481b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_CLEANUP );
5491b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5501b8adde7SWilliam Kucharski 	return success;
5511b8adde7SWilliam Kucharski }
5521b8adde7SWilliam Kucharski 
5531b8adde7SWilliam Kucharski int eb_pxenv_undi_initialize ( void ) {
5541b8adde7SWilliam Kucharski 	int success = 0;
5551b8adde7SWilliam Kucharski 
5561b8adde7SWilliam Kucharski 	undi.pxs->undi_initialize.ProtocolIni = 0;
5571b8adde7SWilliam Kucharski 	memset ( &undi.pxs->undi_initialize.reserved, 0,
5581b8adde7SWilliam Kucharski 		 sizeof ( undi.pxs->undi_initialize.reserved ) );
5591b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
5601b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_INITIALIZE );
5611b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5621b8adde7SWilliam Kucharski 	if ( success ) undi.initialized = 1;
5631b8adde7SWilliam Kucharski 	return success;
5641b8adde7SWilliam Kucharski }
5651b8adde7SWilliam Kucharski 
5661b8adde7SWilliam Kucharski int eb_pxenv_undi_shutdown ( void ) {
5671b8adde7SWilliam Kucharski 	int success = 0;
5681b8adde7SWilliam Kucharski 
5691b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
5701b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_SHUTDOWN );
5711b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5721b8adde7SWilliam Kucharski 	if ( success ) {
5731b8adde7SWilliam Kucharski 		undi.initialized = 0;
5741b8adde7SWilliam Kucharski 		undi.started = 0;
5751b8adde7SWilliam Kucharski 	}
5761b8adde7SWilliam Kucharski 	return success;
5771b8adde7SWilliam Kucharski }
5781b8adde7SWilliam Kucharski 
5791b8adde7SWilliam Kucharski int eb_pxenv_undi_open ( void ) {
5801b8adde7SWilliam Kucharski 	int success = 0;
5811b8adde7SWilliam Kucharski 
5821b8adde7SWilliam Kucharski 	undi.pxs->undi_open.OpenFlag = 0;
5831b8adde7SWilliam Kucharski 	undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
5841b8adde7SWilliam Kucharski 
5851b8adde7SWilliam Kucharski 	/* Multicast support not yet implemented */
5861b8adde7SWilliam Kucharski 	undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0;
5871b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
5881b8adde7SWilliam Kucharski 	      "MCastAddrCount=%hx\n",
5891b8adde7SWilliam Kucharski 	      undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter,
5901b8adde7SWilliam Kucharski 	      undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount );
5911b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_OPEN );
5921b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5931b8adde7SWilliam Kucharski 	if ( success ) undi.opened = 1;
5941b8adde7SWilliam Kucharski 	return success;
5951b8adde7SWilliam Kucharski }
5961b8adde7SWilliam Kucharski 
5971b8adde7SWilliam Kucharski int eb_pxenv_undi_close ( void ) {
5981b8adde7SWilliam Kucharski 	int success = 0;
5991b8adde7SWilliam Kucharski 
6001b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
6011b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_CLOSE );
6021b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
6031b8adde7SWilliam Kucharski 	if ( success ) undi.opened = 0;
6041b8adde7SWilliam Kucharski 	return success;
6051b8adde7SWilliam Kucharski }
6061b8adde7SWilliam Kucharski 
6071b8adde7SWilliam Kucharski int eb_pxenv_undi_transmit_packet ( void ) {
6081b8adde7SWilliam Kucharski 	int success = 0;
6091b8adde7SWilliam Kucharski 	static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
6101b8adde7SWilliam Kucharski 
6111b8adde7SWilliam Kucharski 	/* XMitFlag selects unicast / broadcast */
6121b8adde7SWilliam Kucharski 	if ( memcmp ( undi.xmit_data->destaddr, broadcast,
6131b8adde7SWilliam Kucharski 		      sizeof(broadcast) ) == 0 ) {
6141b8adde7SWilliam Kucharski 		undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST;
6151b8adde7SWilliam Kucharski 	} else {
6161b8adde7SWilliam Kucharski 		undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
6171b8adde7SWilliam Kucharski 	}
6181b8adde7SWilliam Kucharski 
6191b8adde7SWilliam Kucharski 	/* Zero reserved dwords */
6201b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.Reserved[0] = 0;
6211b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.Reserved[1] = 0;
6221b8adde7SWilliam Kucharski 
6231b8adde7SWilliam Kucharski 	/* Segment:offset pointer to DestAddr in base memory */
6241b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.DestAddr.segment =
6251b8adde7SWilliam Kucharski 		SEGMENT( undi.xmit_data->destaddr );
6261b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.DestAddr.offset =
6271b8adde7SWilliam Kucharski 		OFFSET( undi.xmit_data->destaddr );
6281b8adde7SWilliam Kucharski 
6291b8adde7SWilliam Kucharski 	/* Segment:offset pointer to TBD in base memory */
6301b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd );
6311b8adde7SWilliam Kucharski 	undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd );
6321b8adde7SWilliam Kucharski 
6331b8adde7SWilliam Kucharski 	/* Use only the "immediate" part of the TBD */
6341b8adde7SWilliam Kucharski 	undi.xmit_data->tbd.DataBlkCount = 0;
6351b8adde7SWilliam Kucharski 
6361b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
6371b8adde7SWilliam Kucharski 	      "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
6381b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.Protocol,
6391b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.XmitFlag,
6401b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.DestAddr.segment,
6411b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.DestAddr.offset,
6421b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.TBD.segment,
6431b8adde7SWilliam Kucharski 	      undi.pxs->undi_transmit.TBD.offset );
6441b8adde7SWilliam Kucharski 	DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
6451b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.ImmedLength,
6461b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.Xmit.segment,
6471b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.Xmit.offset,
6481b8adde7SWilliam Kucharski 	      undi.xmit_data->tbd.DataBlkCount );
6491b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_TRANSMIT );
6501b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
6511b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs) );
6521b8adde7SWilliam Kucharski 	return success;
6531b8adde7SWilliam Kucharski }
6541b8adde7SWilliam Kucharski 
6551b8adde7SWilliam Kucharski int eb_pxenv_undi_set_station_address ( void ) {
6561b8adde7SWilliam Kucharski 	/* This will spuriously fail on some cards.  Ignore failures.
6571b8adde7SWilliam Kucharski 	 * We only ever use it to set the MAC address to the card's
6581b8adde7SWilliam Kucharski 	 * permanent value anyway, so it's a useless call (although we
6591b8adde7SWilliam Kucharski 	 * make it because PXE spec says we should).
6601b8adde7SWilliam Kucharski 	 */
6611b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
6621b8adde7SWilliam Kucharski 	      "StationAddress=%!\n",
6631b8adde7SWilliam Kucharski 	      undi.pxs->undi_set_station_address.StationAddress );
6641b8adde7SWilliam Kucharski 	undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS );
6651b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
6661b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs) );
6671b8adde7SWilliam Kucharski 	return 1;
6681b8adde7SWilliam Kucharski }
6691b8adde7SWilliam Kucharski 
6701b8adde7SWilliam Kucharski int eb_pxenv_undi_get_information ( void ) {
6711b8adde7SWilliam Kucharski 	int success = 0;
6721b8adde7SWilliam Kucharski 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
6731b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
6741b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_GET_INFORMATION );
6751b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
6761b8adde7SWilliam Kucharski 	      "BaseIO=%hx IntNumber=%hx ...\n"
6771b8adde7SWilliam Kucharski 	      "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
6781b8adde7SWilliam Kucharski 	      "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
6791b8adde7SWilliam Kucharski 	      "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
6801b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
6811b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.BaseIo,
6821b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.IntNumber,
6831b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.MaxTranUnit,
6841b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.HwType,
6851b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.HwAddrLen,
6861b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.CurrentNodeAddress,
6871b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.PermNodeAddress,
6881b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.ROMAddress,
6891b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.RxBufCt,
6901b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_information.TxBufCt );
6911b8adde7SWilliam Kucharski 	return success;
6921b8adde7SWilliam Kucharski }
6931b8adde7SWilliam Kucharski 
6941b8adde7SWilliam Kucharski int eb_pxenv_undi_get_iface_info ( void ) {
6951b8adde7SWilliam Kucharski 	int success = 0;
6961b8adde7SWilliam Kucharski 
6971b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
6981b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_GET_IFACE_INFO );
6991b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
7001b8adde7SWilliam Kucharski 	      "... LinkSpeed=%x ServiceFlags=%x\n",
7011b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
7021b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.IfaceType,
7031b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.LinkSpeed,
7041b8adde7SWilliam Kucharski 	      undi.pxs->undi_get_iface_info.ServiceFlags );
7051b8adde7SWilliam Kucharski 	return success;
7061b8adde7SWilliam Kucharski }
7071b8adde7SWilliam Kucharski 
7081b8adde7SWilliam Kucharski int eb_pxenv_undi_isr ( void ) {
7091b8adde7SWilliam Kucharski 	int success = 0;
7101b8adde7SWilliam Kucharski 
7111b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
7121b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FuncFlag );
7131b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_UNDI_ISR );
7141b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
7151b8adde7SWilliam Kucharski 	      "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
7161b8adde7SWilliam Kucharski 	      "ProtType=%hhx ...\n... PktType=%hhx\n",
7171b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag,
7181b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.BufferLength,
7191b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FrameLength,
7201b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.FrameHeaderLength,
7211b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.Frame.segment,
7221b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.Frame.offset,
7231b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.ProtType,
7241b8adde7SWilliam Kucharski 	      undi.pxs->undi_isr.PktType );
7251b8adde7SWilliam Kucharski 	return success;
7261b8adde7SWilliam Kucharski }
7271b8adde7SWilliam Kucharski 
7281b8adde7SWilliam Kucharski int eb_pxenv_stop_undi ( void ) {
7291b8adde7SWilliam Kucharski 	int success = 0;
7301b8adde7SWilliam Kucharski 
7311b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_UNDI => (void)\n" );
7321b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_STOP_UNDI );
7331b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
7341b8adde7SWilliam Kucharski 	if ( success ) undi.prestarted = 0;
7351b8adde7SWilliam Kucharski 	return success;
7361b8adde7SWilliam Kucharski }
7371b8adde7SWilliam Kucharski 
7381b8adde7SWilliam Kucharski int eb_pxenv_unload_stack ( void ) {
7391b8adde7SWilliam Kucharski 	int success = 0;
7401b8adde7SWilliam Kucharski 
7411b8adde7SWilliam Kucharski 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
7421b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
7431b8adde7SWilliam Kucharski 	success = undi_call_silent ( PXENV_UNLOAD_STACK );
7441b8adde7SWilliam Kucharski 	DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
7451b8adde7SWilliam Kucharski 	      UNDI_STATUS(undi.pxs),
7461b8adde7SWilliam Kucharski 	      ( undi.pxs->Status == PXENV_STATUS_SUCCESS ?
7471b8adde7SWilliam Kucharski 		"base-code is ready to be removed" :
7481b8adde7SWilliam Kucharski 		( undi.pxs->Status == PXENV_STATUS_FAILURE ?
7491b8adde7SWilliam Kucharski 		  "the size of free base memory has been changed" :
7501b8adde7SWilliam Kucharski 		  ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ?
7511b8adde7SWilliam Kucharski 		    "the NIC interrupt vector has been changed" :
7521b8adde7SWilliam Kucharski 		    "UNEXPECTED STATUS CODE" ) ) ) );
7531b8adde7SWilliam Kucharski 	return success;
7541b8adde7SWilliam Kucharski }
7551b8adde7SWilliam Kucharski 
7561b8adde7SWilliam Kucharski int eb_pxenv_stop_base ( void ) {
7571b8adde7SWilliam Kucharski 	int success = 0;
7581b8adde7SWilliam Kucharski 
7591b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_BASE => (void)\n" );
7601b8adde7SWilliam Kucharski 	success = undi_call ( PXENV_STOP_BASE );
7611b8adde7SWilliam Kucharski 	DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
7621b8adde7SWilliam Kucharski 	return success;
7631b8adde7SWilliam Kucharski }
7641b8adde7SWilliam Kucharski 
7651b8adde7SWilliam Kucharski /* Unload UNDI base code (if any present) and free memory.
7661b8adde7SWilliam Kucharski  */
7671b8adde7SWilliam Kucharski int undi_unload_base_code ( void ) {
7681b8adde7SWilliam Kucharski 	/* In GRUB, we do not allocate anything, but we still can call
7691b8adde7SWilliam Kucharski 	 * to free the base space */
7701b8adde7SWilliam Kucharski 	void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 );
7711b8adde7SWilliam Kucharski 	size_t bc_code_size = undi.pxe->BC_Code.Seg_Size;
7721b8adde7SWilliam Kucharski 	void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 );
7731b8adde7SWilliam Kucharski 	size_t bc_data_size = undi.pxe->BC_Data.Seg_Size;
7741b8adde7SWilliam Kucharski 	void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 );
7751b8adde7SWilliam Kucharski 	size_t bc_stck_size = undi.pxe->Stack.Seg_Size;
7761b8adde7SWilliam Kucharski 	firing_squad_lineup_t lineup;
7771b8adde7SWilliam Kucharski 
7781b8adde7SWilliam Kucharski 	/* Don't unload if there is no base code present */
7791b8adde7SWilliam Kucharski 	if ( undi.pxe->BC_Code.Seg_Addr == 0 ) return 1;
7801b8adde7SWilliam Kucharski 
7811b8adde7SWilliam Kucharski 	/* Since we never start the base code, the only time we should
7821b8adde7SWilliam Kucharski 	 * reach this is if we were loaded via PXE.  There are many
7831b8adde7SWilliam Kucharski 	 * different and conflicting versions of the "correct" way to
7841b8adde7SWilliam Kucharski 	 * unload the PXE base code, several of which appear within
7851b8adde7SWilliam Kucharski 	 * the PXE specification itself.  This one seems to work for
7861b8adde7SWilliam Kucharski 	 * our purposes.
7871b8adde7SWilliam Kucharski 	 */
7881b8adde7SWilliam Kucharski 	eb_pxenv_stop_base();
7891b8adde7SWilliam Kucharski 	//eb_pxenv_unload_stack();
7901b8adde7SWilliam Kucharski /*	if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) &&
7911b8adde7SWilliam Kucharski 	     ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) ) {
7921b8adde7SWilliam Kucharski 		printf ( "Could not free memory allocated to PXE base code: "
7931b8adde7SWilliam Kucharski 			 "possible memory leak\n" );
7941b8adde7SWilliam Kucharski 		return 0;
7951b8adde7SWilliam Kucharski 		}*/
7961b8adde7SWilliam Kucharski 	/* Free data structures.  Forget what the PXE specification
7971b8adde7SWilliam Kucharski 	 * says about how to calculate the new size of base memory;
7981b8adde7SWilliam Kucharski 	 * basemem.c takes care of all that for us.  Note that we also
7991b8adde7SWilliam Kucharski 	 * have to free the stack (even though PXE spec doesn't say
8001b8adde7SWilliam Kucharski 	 * anything about it) because nothing else is going to do so.
8011b8adde7SWilliam Kucharski 	 *
8021b8adde7SWilliam Kucharski 	 * Structures will almost certainly not be kB-aligned and
8031b8adde7SWilliam Kucharski 	 * there's a reasonable chance that the UNDI code or data
8041b8adde7SWilliam Kucharski 	 * portions will lie in the same kB as the base code.  Since
8051b8adde7SWilliam Kucharski 	 * forget_base_memory works only in 1kB increments, this means
8061b8adde7SWilliam Kucharski 	 * we have to do some arcane trickery.
8071b8adde7SWilliam Kucharski 	 */
8081b8adde7SWilliam Kucharski 	memset ( &lineup, 0, sizeof(lineup) );
8091b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_code) != 0 )
8101b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT );
8111b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_data) != 0 )
8121b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT );
8131b8adde7SWilliam Kucharski 	if ( SEGMENT(bc_stck) != 0 )
8141b8adde7SWilliam Kucharski 		assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT );
8151b8adde7SWilliam Kucharski 	/* Don't shoot any bits of the UNDI driver code or data */
8161b8adde7SWilliam Kucharski 	assemble_firing_squad ( &lineup,
8171b8adde7SWilliam Kucharski 				VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0),
8181b8adde7SWilliam Kucharski 				undi.pxe->UNDICode.Seg_Size, DONTSHOOT );
8191b8adde7SWilliam Kucharski 	assemble_firing_squad ( &lineup,
8201b8adde7SWilliam Kucharski 				VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0),
8211b8adde7SWilliam Kucharski 				undi.pxe->UNDIData.Seg_Size, DONTSHOOT );
8221b8adde7SWilliam Kucharski 	//shoot_targets ( &lineup );
8231b8adde7SWilliam Kucharski 	//undi.pxe->BC_Code.Seg_Addr = 0;
8241b8adde7SWilliam Kucharski 	//undi.pxe->BC_Data.Seg_Addr = 0;
8251b8adde7SWilliam Kucharski 	//undi.pxe->Stack.Seg_Addr = 0;
8261b8adde7SWilliam Kucharski 
8271b8adde7SWilliam Kucharski 	/* Free and reallocate our own base memory data structures, to
8281b8adde7SWilliam Kucharski 	 * allow the freed base-code blocks to be fully released.
8291b8adde7SWilliam Kucharski 	 */
8301b8adde7SWilliam Kucharski 	free_base_mem_data();
8311b8adde7SWilliam Kucharski 	if ( ! allocate_base_mem_data() ) {
8321b8adde7SWilliam Kucharski 		printf ( "FATAL: memory unaccountably lost\n" );
8331b8adde7SWilliam Kucharski 		while ( 1 ) {};
8341b8adde7SWilliam Kucharski 	}
8351b8adde7SWilliam Kucharski 
8361b8adde7SWilliam Kucharski 	return 1;
8371b8adde7SWilliam Kucharski }
8381b8adde7SWilliam Kucharski 
8391b8adde7SWilliam Kucharski /* UNDI full initialization
8401b8adde7SWilliam Kucharski  *
8411b8adde7SWilliam Kucharski  * This calls all the various UNDI initialization routines in sequence.
8421b8adde7SWilliam Kucharski  */
8431b8adde7SWilliam Kucharski 
8441b8adde7SWilliam Kucharski int undi_full_startup ( void ) {
8451b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_start_undi() ) return 0;
8461b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_startup() ) return 0;
8471b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_initialize() ) return 0;
8481b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_get_information() ) return 0;
8491b8adde7SWilliam Kucharski 	undi.irq = undi.pxs->undi_get_information.IntNumber;
8501b8adde7SWilliam Kucharski 	if ( ! install_undi_irq_handler ( undi.irq, undi.pxe->EntryPointSP ) ) {
8511b8adde7SWilliam Kucharski 		undi.irq = IRQ_NONE;
8521b8adde7SWilliam Kucharski 		return 0;
8531b8adde7SWilliam Kucharski 	}
8541b8adde7SWilliam Kucharski 	memmove ( &undi.pxs->undi_set_station_address.StationAddress,
8551b8adde7SWilliam Kucharski 		  &undi.pxs->undi_get_information.PermNodeAddress,
8561b8adde7SWilliam Kucharski 		  sizeof (undi.pxs->undi_set_station_address.StationAddress) );
8571b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_set_station_address() ) return 0;
8581b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_open() ) return 0;
8591b8adde7SWilliam Kucharski 	/* install_undi_irq_handler leaves irq disabled */
8601b8adde7SWilliam Kucharski 	enable_irq ( undi.irq );
8611b8adde7SWilliam Kucharski 	return 1;
8621b8adde7SWilliam Kucharski }
8631b8adde7SWilliam Kucharski 
8641b8adde7SWilliam Kucharski /* UNDI full shutdown
8651b8adde7SWilliam Kucharski  *
8661b8adde7SWilliam Kucharski  * This calls all the various UNDI shutdown routines in sequence and
8671b8adde7SWilliam Kucharski  * also frees any memory that it can.
8681b8adde7SWilliam Kucharski  */
8691b8adde7SWilliam Kucharski 
8701b8adde7SWilliam Kucharski int undi_full_shutdown ( void ) {
8711b8adde7SWilliam Kucharski 	if ( undi.pxe != NULL ) {
8721b8adde7SWilliam Kucharski 		/* In case we didn't allocate the driver's memory in the first
8731b8adde7SWilliam Kucharski 		 * place, try to grab the code and data segments and sizes
8741b8adde7SWilliam Kucharski 		 * from the !PXE structure.
8751b8adde7SWilliam Kucharski 		 */
8761b8adde7SWilliam Kucharski 		if ( undi.driver_code == NULL ) {
8771b8adde7SWilliam Kucharski 			undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
8781b8adde7SWilliam Kucharski 						   0 );
8791b8adde7SWilliam Kucharski 			undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
8801b8adde7SWilliam Kucharski 		}
8811b8adde7SWilliam Kucharski 		if ( undi.driver_data == NULL ) {
8821b8adde7SWilliam Kucharski 			undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
8831b8adde7SWilliam Kucharski 						   0 );
8841b8adde7SWilliam Kucharski 			undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
8851b8adde7SWilliam Kucharski 		}
8861b8adde7SWilliam Kucharski 
8871b8adde7SWilliam Kucharski 		/* Ignore errors and continue in the hope of shutting
8881b8adde7SWilliam Kucharski 		 * down anyway
8891b8adde7SWilliam Kucharski 		 */
8901b8adde7SWilliam Kucharski 		if ( undi.opened ) eb_pxenv_undi_close();
8911b8adde7SWilliam Kucharski 		if ( undi.started ) {
8921b8adde7SWilliam Kucharski 			eb_pxenv_undi_cleanup();
8931b8adde7SWilliam Kucharski 			/* We may get spurious UNDI API errors at this
8941b8adde7SWilliam Kucharski 			 * point.  If startup() succeeded but
8951b8adde7SWilliam Kucharski 			 * initialize() failed then according to the
8961b8adde7SWilliam Kucharski 			 * spec, we should call shutdown().  However,
8971b8adde7SWilliam Kucharski 			 * some NICS will fail with a status code
8981b8adde7SWilliam Kucharski 			 * 0x006a (INVALID_STATE).
8991b8adde7SWilliam Kucharski 			 */
9001b8adde7SWilliam Kucharski 			eb_pxenv_undi_shutdown();
9011b8adde7SWilliam Kucharski 		}
9021b8adde7SWilliam Kucharski 		if ( undi.irq != IRQ_NONE ) {
9031b8adde7SWilliam Kucharski 			remove_undi_irq_handler ( undi.irq );
9041b8adde7SWilliam Kucharski 			undi.irq = IRQ_NONE;
9051b8adde7SWilliam Kucharski 		}
9061b8adde7SWilliam Kucharski 		undi_unload_base_code();
9071b8adde7SWilliam Kucharski 		if ( undi.prestarted ) {
9081b8adde7SWilliam Kucharski 			eb_pxenv_stop_undi();
9091b8adde7SWilliam Kucharski 			/* Success OR Failure indicates that memory
9101b8adde7SWilliam Kucharski 			 * can be freed.  Any other status code means
9111b8adde7SWilliam Kucharski 			 * that it can't.
9121b8adde7SWilliam Kucharski 			 */
9131b8adde7SWilliam Kucharski 			if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) ||
9141b8adde7SWilliam Kucharski 			    ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) {
9151b8adde7SWilliam Kucharski 				printf ("Could not free memory allocated to "
9161b8adde7SWilliam Kucharski 					"UNDI driver: possible memory leak\n");
9171b8adde7SWilliam Kucharski 				return 0;
9181b8adde7SWilliam Kucharski 			}
9191b8adde7SWilliam Kucharski 		}
9201b8adde7SWilliam Kucharski 	}
9211b8adde7SWilliam Kucharski 	/* Free memory allocated to UNDI driver */
9221b8adde7SWilliam Kucharski 	if ( undi.driver_code != NULL ) {
9231b8adde7SWilliam Kucharski 		/* Clear contents in order to eliminate !PXE and PXENV
9241b8adde7SWilliam Kucharski 		 * signatures to prevent spurious detection via base
9251b8adde7SWilliam Kucharski 		 * memory scan.
9261b8adde7SWilliam Kucharski 		 */
9271b8adde7SWilliam Kucharski 		memset ( undi.driver_code, 0, undi.driver_code_size );
9281b8adde7SWilliam Kucharski 		/* forget_base_memory ( undi.driver_code, undi.driver_code_size ); */
9291b8adde7SWilliam Kucharski 		undi.driver_code = NULL;
9301b8adde7SWilliam Kucharski 		undi.driver_code_size = 0;
9311b8adde7SWilliam Kucharski 	}
9321b8adde7SWilliam Kucharski 	if ( undi.driver_data != NULL ) {
9331b8adde7SWilliam Kucharski 		/* forget_base_memory ( undi.driver_data, undi.driver_data_size ); */
9341b8adde7SWilliam Kucharski 		undi.driver_data = NULL;
9351b8adde7SWilliam Kucharski 		undi.driver_data_size = 0;
9361b8adde7SWilliam Kucharski 	}
9371b8adde7SWilliam Kucharski 	/* !PXE structure now gone; memory freed */
9381b8adde7SWilliam Kucharski 	undi.pxe = NULL;
9391b8adde7SWilliam Kucharski 	return 1;
9401b8adde7SWilliam Kucharski }
9411b8adde7SWilliam Kucharski 
9421b8adde7SWilliam Kucharski /**************************************************************************
9431b8adde7SWilliam Kucharski POLL - Wait for a frame
9441b8adde7SWilliam Kucharski ***************************************************************************/
9451b8adde7SWilliam Kucharski static int undi_poll(struct nic *nic, int retrieve)
9461b8adde7SWilliam Kucharski {
9471b8adde7SWilliam Kucharski 	/* Fun, fun, fun.  UNDI drivers don't use polling; they use
9481b8adde7SWilliam Kucharski 	 * interrupts.  We therefore cheat and pretend that an
9491b8adde7SWilliam Kucharski 	 * interrupt has occurred every time undi_poll() is called.
9501b8adde7SWilliam Kucharski 	 * This isn't too much of a hack; PCI devices share IRQs and
9511b8adde7SWilliam Kucharski 	 * so the first thing that a proper ISR should do is call
9521b8adde7SWilliam Kucharski 	 * PXENV_UNDI_ISR to determine whether or not the UNDI NIC
9531b8adde7SWilliam Kucharski 	 * generated the interrupt; there is no harm done by spurious
9541b8adde7SWilliam Kucharski 	 * calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
9551b8adde7SWilliam Kucharski 	 * handling them any more rapidly than the usual rate of
9561b8adde7SWilliam Kucharski 	 * undi_poll() being called even if we did implement a full
9571b8adde7SWilliam Kucharski 	 * ISR.  So it should work.  Ha!
9581b8adde7SWilliam Kucharski 	 *
9591b8adde7SWilliam Kucharski 	 * Addendum (21/10/03).  Some cards don't play nicely with
9601b8adde7SWilliam Kucharski 	 * this trick, so instead of doing it the easy way we have to
9611b8adde7SWilliam Kucharski 	 * go to all the hassle of installing a genuine interrupt
9621b8adde7SWilliam Kucharski 	 * service routine and dealing with the wonderful 8259
9631b8adde7SWilliam Kucharski 	 * Programmable Interrupt Controller.  Joy.
9641b8adde7SWilliam Kucharski 	 *
9651b8adde7SWilliam Kucharski 	 * (02/01/2005). A real UNDI ISR is now implemented in,
9661b8adde7SWilliam Kucharski 	 * following Figure 3-4 in PXE spec 2.0.  The interrupt
9671b8adde7SWilliam Kucharski 	 * handler, undi_irq_handler, issues PXENV_UNDI_ISR_IN_START.
9681b8adde7SWilliam Kucharski 	 * If the interrupt is ours, the handler sends EOI and bumps the
9691b8adde7SWilliam Kucharski 	 * undi_irq_trigger_count. This polled routine is equivalent
9701b8adde7SWilliam Kucharski 	 * to the "driver strategy routine".
9711b8adde7SWilliam Kucharski 	 *
9721b8adde7SWilliam Kucharski 	 * Another issue is that upper layer await_*() does not handle
9731b8adde7SWilliam Kucharski 	 * coalesced packets. The UNDI implementation on broadcom chips
9741b8adde7SWilliam Kucharski 	 * appear to combine interrupts. If we loop through GET_NEXT,
9751b8adde7SWilliam Kucharski 	 * we may hand up coalesced packets, resulting in drops, and
9761b8adde7SWilliam Kucharski 	 * severe time delay. As a temperary hack, we return as soon as
9771b8adde7SWilliam Kucharski 	 * we get something, remembering where we were (IN_PROCESS
9781b8adde7SWilliam Kucharski 	 * or GET_NEXT). This assume packets are never broken up.
9791b8adde7SWilliam Kucharski 	 * XXX Need to fix upper layer to handle coalesced data.
9801b8adde7SWilliam Kucharski 	 */
9811b8adde7SWilliam Kucharski 
9821b8adde7SWilliam Kucharski 	static int undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
9831b8adde7SWilliam Kucharski 
9841b8adde7SWilliam Kucharski 	/* See if a hardware interrupt has occurred since the last poll().
9851b8adde7SWilliam Kucharski 	 */
9861b8adde7SWilliam Kucharski 	switch ( undi_opcode ) {
9871b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_IN_PROCESS:
9881b8adde7SWilliam Kucharski 		if ( ! undi_irq_triggered ( undi.irq ) )
9891b8adde7SWilliam Kucharski 			return 0;
9901b8adde7SWilliam Kucharski 	default:
9911b8adde7SWilliam Kucharski 		break;
9921b8adde7SWilliam Kucharski 	}
9931b8adde7SWilliam Kucharski 
9941b8adde7SWilliam Kucharski 	/* We have an interrupt or there is something left from
9951b8adde7SWilliam Kucharski 	 * last poll. Either way, we need to call UNDI ISR.
9961b8adde7SWilliam Kucharski 	 */
9971b8adde7SWilliam Kucharski 	nic->packetlen = 0;
9981b8adde7SWilliam Kucharski 	undi.pxs->undi_isr.FuncFlag = undi_opcode;
9991b8adde7SWilliam Kucharski 	/* there is no good way to handle this error */
10001b8adde7SWilliam Kucharski 	if ( ! eb_pxenv_undi_isr() ) {
10011b8adde7SWilliam Kucharski 		printf ("undi isr call failed: opcode = %d\n", undi_opcode);
10021b8adde7SWilliam Kucharski 		return 0;
10031b8adde7SWilliam Kucharski 	}
10041b8adde7SWilliam Kucharski 	switch ( undi.pxs->undi_isr.FuncFlag ) {
10051b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_DONE:
10061b8adde7SWilliam Kucharski 		/* Set opcode back to IN_PROCESS and wait for next intr */
10071b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
10081b8adde7SWilliam Kucharski 		return 0;
10091b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_TRANSMIT:
10101b8adde7SWilliam Kucharski 		/* We really don't care about transmission complete
10111b8adde7SWilliam Kucharski 		 * interrupts. Move on to next frame.
10121b8adde7SWilliam Kucharski 		 */
10131b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10141b8adde7SWilliam Kucharski 		return 0;
10151b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_BUSY:
10161b8adde7SWilliam Kucharski 		/* This should never happen.
10171b8adde7SWilliam Kucharski 		 */
10181b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10191b8adde7SWilliam Kucharski 		printf ( "UNDI ISR thinks it's being re-entered!\n"
10201b8adde7SWilliam Kucharski 			 "Aborting receive\n" );
10211b8adde7SWilliam Kucharski 		return 0;
10221b8adde7SWilliam Kucharski 	case PXENV_UNDI_ISR_OUT_RECEIVE:
10231b8adde7SWilliam Kucharski 		/* Copy data to receive buffer and move on to next frame */
10241b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10251b8adde7SWilliam Kucharski 		memcpy ( nic->packet + nic->packetlen,
10261b8adde7SWilliam Kucharski 			 VIRTUAL( undi.pxs->undi_isr.Frame.segment,
10271b8adde7SWilliam Kucharski 				  undi.pxs->undi_isr.Frame.offset ),
10281b8adde7SWilliam Kucharski 			 undi.pxs->undi_isr.BufferLength );
10291b8adde7SWilliam Kucharski 		nic->packetlen += undi.pxs->undi_isr.BufferLength;
10301b8adde7SWilliam Kucharski 		break;
10311b8adde7SWilliam Kucharski 	default:
10321b8adde7SWilliam Kucharski 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
10331b8adde7SWilliam Kucharski 		printf ( "UNDI ISR returned bizzare status code %d\n",
10341b8adde7SWilliam Kucharski 			 undi.pxs->undi_isr.FuncFlag );
10351b8adde7SWilliam Kucharski 	}
10361b8adde7SWilliam Kucharski 
10371b8adde7SWilliam Kucharski 	return nic->packetlen > 0 ? 1 : 0;
10381b8adde7SWilliam Kucharski }
10391b8adde7SWilliam Kucharski 
10401b8adde7SWilliam Kucharski /**************************************************************************
10411b8adde7SWilliam Kucharski TRANSMIT - Transmit a frame
10421b8adde7SWilliam Kucharski ***************************************************************************/
10431b8adde7SWilliam Kucharski static void undi_transmit(
10441b8adde7SWilliam Kucharski 	struct nic *nic,
10451b8adde7SWilliam Kucharski 	const char *d,			/* Destination */
10461b8adde7SWilliam Kucharski 	unsigned int t,			/* Type */
10471b8adde7SWilliam Kucharski 	unsigned int s,			/* size */
10481b8adde7SWilliam Kucharski 	const char *p)			/* Packet */
10491b8adde7SWilliam Kucharski {
10501b8adde7SWilliam Kucharski 	/* Inhibit compiler warning about unused parameter nic */
10511b8adde7SWilliam Kucharski 	if ( nic == NULL ) {};
10521b8adde7SWilliam Kucharski 
10531b8adde7SWilliam Kucharski 	/* Copy destination to buffer in base memory */
10541b8adde7SWilliam Kucharski 	memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
10551b8adde7SWilliam Kucharski 
10561b8adde7SWilliam Kucharski 	/* Translate packet type to UNDI packet type */
10571b8adde7SWilliam Kucharski 	switch ( t ) {
10581b8adde7SWilliam Kucharski 	case IP :  undi.pxs->undi_transmit.Protocol = P_IP;   break;
10591b8adde7SWilliam Kucharski 	case ARP:  undi.pxs->undi_transmit.Protocol = P_ARP;  break;
10601b8adde7SWilliam Kucharski 	case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break;
10611b8adde7SWilliam Kucharski 	default: undi.pxs->undi_transmit.Protocol = P_UNKNOWN; break;
10621b8adde7SWilliam Kucharski 	}
10631b8adde7SWilliam Kucharski 
10641b8adde7SWilliam Kucharski 	/* Store packet length in TBD */
10651b8adde7SWilliam Kucharski 	undi.xmit_data->tbd.ImmedLength = s;
10661b8adde7SWilliam Kucharski 
10671b8adde7SWilliam Kucharski 	/* Check to see if data to be transmitted is currently in base
10681b8adde7SWilliam Kucharski 	 * memory.  If not, allocate temporary storage in base memory
10691b8adde7SWilliam Kucharski 	 * and copy it there.
10701b8adde7SWilliam Kucharski 	 */
10711b8adde7SWilliam Kucharski 	if ( SEGMENT( p ) <= 0xffff ) {
10721b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
10731b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
10741b8adde7SWilliam Kucharski 	} else {
10751b8adde7SWilliam Kucharski 		memcpy ( undi.xmit_buffer, p, s );
10761b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer );
10771b8adde7SWilliam Kucharski 		undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer );
10781b8adde7SWilliam Kucharski 	}
10791b8adde7SWilliam Kucharski 
10801b8adde7SWilliam Kucharski 	eb_pxenv_undi_transmit_packet();
10811b8adde7SWilliam Kucharski }
10821b8adde7SWilliam Kucharski 
10831b8adde7SWilliam Kucharski /**************************************************************************
10841b8adde7SWilliam Kucharski DISABLE - Turn off ethernet interface
10851b8adde7SWilliam Kucharski ***************************************************************************/
10861b8adde7SWilliam Kucharski static void undi_disable(struct dev *dev)
10871b8adde7SWilliam Kucharski {
10881b8adde7SWilliam Kucharski 	/* Inhibit compiler warning about unused parameter dev */
10891b8adde7SWilliam Kucharski 	if ( dev == NULL ) {};
10901b8adde7SWilliam Kucharski 	undi_full_shutdown();
10911b8adde7SWilliam Kucharski 	free_base_mem_data();
10921b8adde7SWilliam Kucharski }
10931b8adde7SWilliam Kucharski 
10941b8adde7SWilliam Kucharski /**************************************************************************
10951b8adde7SWilliam Kucharski PROBE - Look for an adapter, this routine's visible to the outside
10961b8adde7SWilliam Kucharski ***************************************************************************/
10971b8adde7SWilliam Kucharski 
10981b8adde7SWilliam Kucharski /* Locate an UNDI driver by first scanning through base memory for an
10991b8adde7SWilliam Kucharski  * installed driver and then by scanning for UNDI ROMs and attempting
11001b8adde7SWilliam Kucharski  * to install their drivers.
11011b8adde7SWilliam Kucharski  */
11021b8adde7SWilliam Kucharski 
11031b8adde7SWilliam Kucharski int hunt_pixies_and_undi_roms ( void ) {
11041b8adde7SWilliam Kucharski 	static uint8_t hunt_type = HUNT_FOR_PIXIES;
11051b8adde7SWilliam Kucharski 
11061b8adde7SWilliam Kucharski 	if ( hunt_type == HUNT_FOR_PIXIES ) {
11071b8adde7SWilliam Kucharski 		if ( hunt_pixie() ) {
11081b8adde7SWilliam Kucharski 			return 1;
11091b8adde7SWilliam Kucharski 		}
11101b8adde7SWilliam Kucharski 	}
11111b8adde7SWilliam Kucharski 	hunt_type = HUNT_FOR_UNDI_ROMS;
11121b8adde7SWilliam Kucharski 	while ( hunt_undi_rom() ) {
11131b8adde7SWilliam Kucharski 		if ( undi_loader() ) {
11141b8adde7SWilliam Kucharski 			return 1;
11151b8adde7SWilliam Kucharski 		}
11161b8adde7SWilliam Kucharski 		undi_full_shutdown(); /* Free any allocated memory */
11171b8adde7SWilliam Kucharski 	}
11181b8adde7SWilliam Kucharski 	hunt_type = HUNT_FOR_PIXIES;
11191b8adde7SWilliam Kucharski 	return 0;
11201b8adde7SWilliam Kucharski }
11211b8adde7SWilliam Kucharski 
11221b8adde7SWilliam Kucharski /* The actual Etherboot probe routine.
11231b8adde7SWilliam Kucharski  */
11241b8adde7SWilliam Kucharski 
11251b8adde7SWilliam Kucharski static int undi_probe(struct dev *dev, struct pci_device *pci)
11261b8adde7SWilliam Kucharski {
11271b8adde7SWilliam Kucharski 	struct nic *nic = (struct nic *)dev;
11281b8adde7SWilliam Kucharski 
11291b8adde7SWilliam Kucharski 	/* Zero out global undi structure */
11301b8adde7SWilliam Kucharski 	memset ( &undi, 0, sizeof(undi) );
11311b8adde7SWilliam Kucharski 
11321b8adde7SWilliam Kucharski 	/* Store PCI parameters; we will need them to initialize the UNDI
11331b8adde7SWilliam Kucharski 	 * driver later.
11341b8adde7SWilliam Kucharski 	 */
11351b8adde7SWilliam Kucharski 	memcpy ( &undi.pci, pci, sizeof(undi.pci) );
11361b8adde7SWilliam Kucharski 
11371b8adde7SWilliam Kucharski 	/* Find the BIOS' $PnP structure */
11381b8adde7SWilliam Kucharski 	if ( ! hunt_pnp_bios() ) {
11391b8adde7SWilliam Kucharski 		printf ( "No PnP BIOS found; aborting\n" );
11401b8adde7SWilliam Kucharski 		return 0;
11411b8adde7SWilliam Kucharski 	}
11421b8adde7SWilliam Kucharski 
11431b8adde7SWilliam Kucharski 	/* Allocate base memory data structures */
11441b8adde7SWilliam Kucharski 	if ( ! allocate_base_mem_data() ) return 0;
11451b8adde7SWilliam Kucharski 
11461b8adde7SWilliam Kucharski 	/* Search thoroughly for UNDI drivers */
11471b8adde7SWilliam Kucharski 	for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
11481b8adde7SWilliam Kucharski 		/* Try to initialise UNDI driver */
11491b8adde7SWilliam Kucharski 		DBG ( "Initializing UNDI driver.  Please wait...\n" );
11501b8adde7SWilliam Kucharski 		if ( ! undi_full_startup() ) {
11511b8adde7SWilliam Kucharski 			if ( undi.pxs->Status ==
11521b8adde7SWilliam Kucharski 			     PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
11531b8adde7SWilliam Kucharski 				DBG ( "Cable not connected (code %#hx)\n",
11541b8adde7SWilliam Kucharski 					 PXENV_STATUS_UNDI_MEDIATEST_FAILED );
11551b8adde7SWilliam Kucharski 			}
11561b8adde7SWilliam Kucharski 			continue;
11571b8adde7SWilliam Kucharski 		}
11581b8adde7SWilliam Kucharski 		/* Basic information: MAC, IO addr, IRQ */
11591b8adde7SWilliam Kucharski 		if ( ! eb_pxenv_undi_get_information() ) continue;
11601b8adde7SWilliam Kucharski 		DBG ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
11611b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.BaseIo,
11621b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.IntNumber,
11631b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.CurrentNodeAddress );
11641b8adde7SWilliam Kucharski 		/* Fill out MAC address in nic structure */
11651b8adde7SWilliam Kucharski 		memcpy ( nic->node_addr,
11661b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_information.CurrentNodeAddress,
11671b8adde7SWilliam Kucharski 			 ETH_ALEN );
11681b8adde7SWilliam Kucharski 		/* More diagnostic information including link speed */
11691b8adde7SWilliam Kucharski 		if ( ! eb_pxenv_undi_get_iface_info() ) continue;
11701b8adde7SWilliam Kucharski 		printf ( "  NDIS type %s interface at %d Mbps\n",
11711b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_iface_info.IfaceType,
11721b8adde7SWilliam Kucharski 			 undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
11731b8adde7SWilliam Kucharski 		DBG ("UNDI Stack at %#hx:%#hx",UNDI_STACK_SEG, UNDI_STACK_OFF);
11741b8adde7SWilliam Kucharski 		dev->disable  = undi_disable;
11751b8adde7SWilliam Kucharski 		nic->poll     = undi_poll;
11761b8adde7SWilliam Kucharski 		nic->transmit = undi_transmit;
11771b8adde7SWilliam Kucharski 		return 1;
11781b8adde7SWilliam Kucharski 	}
11791b8adde7SWilliam Kucharski 	undi_disable ( dev ); /* To free base memory structures */
11801b8adde7SWilliam Kucharski 	return 0;
11811b8adde7SWilliam Kucharski }
11821b8adde7SWilliam Kucharski 
11831b8adde7SWilliam Kucharski /* UNDI driver states that it is suitable for any PCI NIC (i.e. any
11841b8adde7SWilliam Kucharski  * PCI device of class PCI_CLASS_NETWORK_ETHERNET).  If there are any
11851b8adde7SWilliam Kucharski  * obscure UNDI NICs that have the incorrect PCI class, add them to
11861b8adde7SWilliam Kucharski  * this list.
11871b8adde7SWilliam Kucharski  */
11881b8adde7SWilliam Kucharski static struct pci_id undi_nics[] = {
1189*6148443aSJakub Jermar 	PCI_ROM(0x10de, 0x0057, "ck804", "nVidia Corporation CK804 Ethernet"),
1190*6148443aSJakub Jermar 	PCI_ROM(0x10de, 0x0373, "mcp55", "nVidia Corporation MCP55 Ethernet")
11911b8adde7SWilliam Kucharski };
11921b8adde7SWilliam Kucharski 
11931b8adde7SWilliam Kucharski struct pci_driver undi_driver = {
11941b8adde7SWilliam Kucharski 	.type     = NIC_DRIVER,
11951b8adde7SWilliam Kucharski 	.name     = "UNDI",
11961b8adde7SWilliam Kucharski 	.probe    = undi_probe,
11971b8adde7SWilliam Kucharski 	.ids      = undi_nics,
11981b8adde7SWilliam Kucharski  	.id_count = sizeof(undi_nics)/sizeof(undi_nics[0]),
11991b8adde7SWilliam Kucharski 	.class    = PCI_CLASS_NETWORK_ETHERNET,
12001b8adde7SWilliam Kucharski };
12011b8adde7SWilliam Kucharski 
12021b8adde7SWilliam Kucharski /************************************************
12031b8adde7SWilliam Kucharski  * Code for reusing the BIOS provided pxe stack
12041b8adde7SWilliam Kucharski  */
12051b8adde7SWilliam Kucharski 
12061b8adde7SWilliam Kucharski /* Verify !PXE structure saved by pxeloader. */
12071b8adde7SWilliam Kucharski int undi_bios_pxe(void **dhcpreply)
12081b8adde7SWilliam Kucharski {
12091b8adde7SWilliam Kucharski 	pxe_t *pxe;
12101b8adde7SWilliam Kucharski 	uint16_t *ptr = (uint16_t *)0x7C80;
12111b8adde7SWilliam Kucharski 
12121b8adde7SWilliam Kucharski 	pxe = (pxe_t *) VIRTUAL(ptr[0], ptr[1]);
12131b8adde7SWilliam Kucharski 	if (memcmp(pxe->Signature, "!PXE", 4) != 0) {
12141b8adde7SWilliam Kucharski 		DBG ("invalid !PXE signature at %x:%x\n", ptr[0], ptr[1]);
12151b8adde7SWilliam Kucharski 		return 0;
12161b8adde7SWilliam Kucharski 	}
12171b8adde7SWilliam Kucharski 
12181b8adde7SWilliam Kucharski 	if (checksum(pxe, sizeof(pxe_t)) != 0) {
12191b8adde7SWilliam Kucharski 		DBG ("invalid checksum\n");
12201b8adde7SWilliam Kucharski 		return 0;
12211b8adde7SWilliam Kucharski 	}
12221b8adde7SWilliam Kucharski 
12231b8adde7SWilliam Kucharski 	/* Zero out global undi structure */
12241b8adde7SWilliam Kucharski 	memset (&undi, 0, sizeof(undi));
12251b8adde7SWilliam Kucharski 
12261b8adde7SWilliam Kucharski 	/* Allocate base memory data structures */
12271b8adde7SWilliam Kucharski 	if (! allocate_base_mem_data()) return 0;
12281b8adde7SWilliam Kucharski 
12291b8adde7SWilliam Kucharski 	undi.pxe = pxe;
12301b8adde7SWilliam Kucharski 	pxe_dump();
12311b8adde7SWilliam Kucharski 
12321b8adde7SWilliam Kucharski 	if (!eb_pxenv_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, dhcpreply)) {
12331b8adde7SWilliam Kucharski 		DBG ("failed to get cached DHCP reply\n");
12341b8adde7SWilliam Kucharski 		return 0;
12351b8adde7SWilliam Kucharski 	}
12361b8adde7SWilliam Kucharski 	return 1;
12371b8adde7SWilliam Kucharski }
12381b8adde7SWilliam Kucharski 
12391b8adde7SWilliam Kucharski void undi_pxe_disable(void)
12401b8adde7SWilliam Kucharski {
12411b8adde7SWilliam Kucharski 	/* full shutdown is problematic for some machines */
12421b8adde7SWilliam Kucharski 	(void) eb_pxenv_undi_shutdown();
12431b8adde7SWilliam Kucharski }
12441b8adde7SWilliam Kucharski 
12451b8adde7SWilliam Kucharski /*
12461b8adde7SWilliam Kucharski  * Various helper functions for dhcp and tftp
12471b8adde7SWilliam Kucharski  */
12481b8adde7SWilliam Kucharski int eb_pxenv_get_cached_info (uint8_t type, void **info)
12491b8adde7SWilliam Kucharski {
12501b8adde7SWilliam Kucharski 	int success;
12511b8adde7SWilliam Kucharski 
12521b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
12531b8adde7SWilliam Kucharski 	/* Segment:offset pointer to DestAddr in base memory */
12541b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.PacketType = type;
12551b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.BufferSize = 0;
12561b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.Buffer.segment = 0;
12571b8adde7SWilliam Kucharski 	undi.pxs->get_cached_info.Buffer.offset = 0;
12581b8adde7SWilliam Kucharski 
12591b8adde7SWilliam Kucharski 	success = undi_call(PXENV_GET_CACHED_INFO);
12601b8adde7SWilliam Kucharski 	DBG ("PXENV_GET_CACHED_INFO <= Status=%s\n", UNDI_STATUS(undi.pxs));
12611b8adde7SWilliam Kucharski 
12621b8adde7SWilliam Kucharski 	*info = (void *)VIRTUAL(undi.pxs->get_cached_info.Buffer.segment,
12631b8adde7SWilliam Kucharski 	    undi.pxs->get_cached_info.Buffer.offset);
12641b8adde7SWilliam Kucharski 	return success;
12651b8adde7SWilliam Kucharski }
12661b8adde7SWilliam Kucharski 
12671b8adde7SWilliam Kucharski /* tftp help routines */
12681b8adde7SWilliam Kucharski int eb_pxenv_tftp_open(char *file, IP4_t serverip, IP4_t gatewayip,
12691b8adde7SWilliam Kucharski     uint16_t *pktlen)
12701b8adde7SWilliam Kucharski {
12711b8adde7SWilliam Kucharski 	int success;
12721b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
12731b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.ServerIPAddress = serverip;
12741b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
12751b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.TFTPPort = htons(TFTP_PORT);
12761b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.PacketSize = TFTP_MAX_PACKET;
12771b8adde7SWilliam Kucharski 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
12781b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_OPEN);
12791b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs));
12801b8adde7SWilliam Kucharski 	*pktlen = undi.pxs->tftp_open.PacketSize;
12811b8adde7SWilliam Kucharski 	return success;
12821b8adde7SWilliam Kucharski }
12831b8adde7SWilliam Kucharski 
12841b8adde7SWilliam Kucharski int eb_pxenv_tftp_read(uint8_t *buf, uint16_t *len)
12851b8adde7SWilliam Kucharski {
12861b8adde7SWilliam Kucharski 	static int tftp_count = 0;
12871b8adde7SWilliam Kucharski 
12881b8adde7SWilliam Kucharski 	int success;
12891b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
12901b8adde7SWilliam Kucharski 	undi.pxs->tftp_read.Buffer.segment = SEGMENT(buf);
12911b8adde7SWilliam Kucharski 	undi.pxs->tftp_read.Buffer.offset = OFFSET(buf);
12921b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_READ);
12931b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_READ <= Status=%s\n", UNDI_STATUS(undi.pxs));
12941b8adde7SWilliam Kucharski 	*len = undi.pxs->tftp_read.BufferSize;
12951b8adde7SWilliam Kucharski 	tftp_count++;
12961b8adde7SWilliam Kucharski 	if ((tftp_count % 1000) == 0)
12971b8adde7SWilliam Kucharski 		noisy_printf(".");
12981b8adde7SWilliam Kucharski 	return success;
12991b8adde7SWilliam Kucharski }
13001b8adde7SWilliam Kucharski 
13011b8adde7SWilliam Kucharski int eb_pxenv_tftp_close(void)
13021b8adde7SWilliam Kucharski {
13031b8adde7SWilliam Kucharski 	int success;
13041b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
13051b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_CLOSE);
13061b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs));
13071b8adde7SWilliam Kucharski 	return success;
13081b8adde7SWilliam Kucharski }
13091b8adde7SWilliam Kucharski 
13101b8adde7SWilliam Kucharski int eb_pxenv_tftp_get_fsize(char *file, IP4_t serverip, IP4_t gatewayip,
13111b8adde7SWilliam Kucharski     uint32_t *fsize)
13121b8adde7SWilliam Kucharski {
13131b8adde7SWilliam Kucharski 	int success;
13141b8adde7SWilliam Kucharski 	memset(undi.pxs, 0, sizeof (undi.pxs));
13151b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.ServerIPAddress = serverip;
13161b8adde7SWilliam Kucharski 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
13171b8adde7SWilliam Kucharski 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
13181b8adde7SWilliam Kucharski 	success = undi_call(PXENV_TFTP_GET_FSIZE);
13191b8adde7SWilliam Kucharski 	DBG ("PXENV_TFTP_GET_FSIZE <= Status=%s\n", UNDI_STATUS(undi.pxs));
13201b8adde7SWilliam Kucharski 	*fsize = undi.pxs->tftp_get_fsize.FileSize;
13211b8adde7SWilliam Kucharski 	return success;
13221b8adde7SWilliam Kucharski }
1323