17c478bd9Sstevel@tonic-gate /**************************************************************************
27c478bd9Sstevel@tonic-gate Etherboot -  BOOTP/TFTP Bootstrap Program
37c478bd9Sstevel@tonic-gate UNDI NIC driver for Etherboot
47c478bd9Sstevel@tonic-gate 
57c478bd9Sstevel@tonic-gate This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
67c478bd9Sstevel@tonic-gate of Fen Systems Ltd. (http://www.fensystems.co.uk/).  All rights
77c478bd9Sstevel@tonic-gate reserved.
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate $Id: undi.c,v 1.8 2003/10/25 13:54:53 mcb30 Exp $
107c478bd9Sstevel@tonic-gate ***************************************************************************/
117c478bd9Sstevel@tonic-gate 
127c478bd9Sstevel@tonic-gate /*
137c478bd9Sstevel@tonic-gate  * This program is free software; you can redistribute it and/or
147c478bd9Sstevel@tonic-gate  * modify it under the terms of the GNU General Public License as
157c478bd9Sstevel@tonic-gate  * published by the Free Software Foundation; either version 2, or (at
167c478bd9Sstevel@tonic-gate  * your option) any later version.
177c478bd9Sstevel@tonic-gate  */
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate /* to get some global routines like printf */
207c478bd9Sstevel@tonic-gate #include "etherboot.h"
217c478bd9Sstevel@tonic-gate /* to get the interface to the body of the program */
227c478bd9Sstevel@tonic-gate #include "nic.h"
237c478bd9Sstevel@tonic-gate /* to get the PCI support functions, if this is a PCI NIC */
247c478bd9Sstevel@tonic-gate #include "pci.h"
257c478bd9Sstevel@tonic-gate /* UNDI and PXE defines.  Includes pxe.h. */
267c478bd9Sstevel@tonic-gate #include "undi.h"
277c478bd9Sstevel@tonic-gate /* 8259 PIC defines */
287c478bd9Sstevel@tonic-gate #include "pic8259.h"
2999ed6083Sszhou #include "bootp.h"
3099ed6083Sszhou #include "tftp.h"
3199ed6083Sszhou #include "shared.h"
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /* NIC specific static variables go here */
347c478bd9Sstevel@tonic-gate static undi_t undi = { NULL, NULL, NULL, NULL, NULL, NULL, NULL,
357c478bd9Sstevel@tonic-gate 		       NULL, NULL, 0, NULL, 0, NULL,
367c478bd9Sstevel@tonic-gate 		       0, 0, 0, 0,
377c478bd9Sstevel@tonic-gate 		       { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL },
387c478bd9Sstevel@tonic-gate 		       IRQ_NONE };
397c478bd9Sstevel@tonic-gate static undi_base_mem_data_t undi_base_mem_data;
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #define UNDI_HEAP (void *)(512 << 10)
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /* Function prototypes */
447c478bd9Sstevel@tonic-gate int allocate_base_mem_data ( void );
457c478bd9Sstevel@tonic-gate int free_base_mem_data ( void );
467c478bd9Sstevel@tonic-gate int eb_pxenv_undi_shutdown ( void );
477c478bd9Sstevel@tonic-gate int eb_pxenv_stop_undi ( void );
487c478bd9Sstevel@tonic-gate int undi_unload_base_code ( void );
497c478bd9Sstevel@tonic-gate int undi_full_shutdown ( void );
5099ed6083Sszhou int eb_pxenv_get_cached_info (uint8_t, void **info);
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /**************************************************************************
537c478bd9Sstevel@tonic-gate  * Utility functions
547c478bd9Sstevel@tonic-gate  **************************************************************************/
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /* Checksum a block.
577c478bd9Sstevel@tonic-gate  */
587c478bd9Sstevel@tonic-gate 
checksum(void * block,size_t size)597c478bd9Sstevel@tonic-gate uint8_t checksum ( void *block, size_t size ) {
607c478bd9Sstevel@tonic-gate 	uint8_t sum = 0;
617c478bd9Sstevel@tonic-gate 	uint16_t i = 0;
627c478bd9Sstevel@tonic-gate 	for ( i = 0; i < size; i++ ) {
637c478bd9Sstevel@tonic-gate 		sum += ( ( uint8_t * ) block )[i];
647c478bd9Sstevel@tonic-gate 	}
657c478bd9Sstevel@tonic-gate 	return sum;
667c478bd9Sstevel@tonic-gate }
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /* Print the status of a !PXE structure
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate 
pxe_dump(void)717c478bd9Sstevel@tonic-gate void pxe_dump ( void ) {
727c478bd9Sstevel@tonic-gate #ifdef TRACE_UNDI
737c478bd9Sstevel@tonic-gate 	printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
747c478bd9Sstevel@tonic-gate 		 "BD %hx:%hx BC %hx:%hx\n",
757c478bd9Sstevel@tonic-gate 		 undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset,
767c478bd9Sstevel@tonic-gate 		 undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size,
777c478bd9Sstevel@tonic-gate 		 undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size,
787c478bd9Sstevel@tonic-gate 		 undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size,
797c478bd9Sstevel@tonic-gate 		 undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size,
807c478bd9Sstevel@tonic-gate 		 undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size );
817c478bd9Sstevel@tonic-gate #endif
827c478bd9Sstevel@tonic-gate }
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /* Allocate/free space for structures that must reside in base memory
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate 
allocate_base_mem_data(void)877c478bd9Sstevel@tonic-gate int allocate_base_mem_data ( void ) {
887c478bd9Sstevel@tonic-gate 	/* In GRUB, anything is in base address, so we do not need
897c478bd9Sstevel@tonic-gate 	 * allocate anything */
907c478bd9Sstevel@tonic-gate 	undi.base_mem_data = &undi_base_mem_data;
917c478bd9Sstevel@tonic-gate 	memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
927c478bd9Sstevel@tonic-gate 	undi.undi_call_info = &undi.base_mem_data->undi_call_info;
937c478bd9Sstevel@tonic-gate 	undi.pxs = &undi.base_mem_data->pxs;
947c478bd9Sstevel@tonic-gate 	undi.xmit_data = &undi.base_mem_data->xmit_data;
957c478bd9Sstevel@tonic-gate 	undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
967c478bd9Sstevel@tonic-gate #if 0				/* Etherboot Code */
977c478bd9Sstevel@tonic-gate 	/* Allocate space in base memory.
987c478bd9Sstevel@tonic-gate 	 * Initialise pointers to base memory structures.
997c478bd9Sstevel@tonic-gate 	 */
1007c478bd9Sstevel@tonic-gate 	if ( undi.base_mem_data == NULL ) {
1017c478bd9Sstevel@tonic-gate 		undi.base_mem_data =
1027c478bd9Sstevel@tonic-gate 			allot_base_memory ( sizeof(undi_base_mem_data_t) +
1037c478bd9Sstevel@tonic-gate 					    TRIVIAL_IRQ_HANDLER_SIZE );
1047c478bd9Sstevel@tonic-gate 		if ( undi.base_mem_data == NULL ) {
1057c478bd9Sstevel@tonic-gate 			printf ( "Failed to allocate base memory\n" );
1067c478bd9Sstevel@tonic-gate 			free_base_mem_data();
1077c478bd9Sstevel@tonic-gate 			return 0;
1087c478bd9Sstevel@tonic-gate 		}
1097c478bd9Sstevel@tonic-gate 		memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
1107c478bd9Sstevel@tonic-gate 		undi.undi_call_info = &undi.base_mem_data->undi_call_info;
1117c478bd9Sstevel@tonic-gate 		undi.pxs = &undi.base_mem_data->pxs;
1127c478bd9Sstevel@tonic-gate 		undi.xmit_data = &undi.base_mem_data->xmit_data;
1137c478bd9Sstevel@tonic-gate 		undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
1147c478bd9Sstevel@tonic-gate 		copy_trivial_irq_handler ( undi.base_mem_data->irq_handler,
1157c478bd9Sstevel@tonic-gate 					   TRIVIAL_IRQ_HANDLER_SIZE );
1167c478bd9Sstevel@tonic-gate 	}
1177c478bd9Sstevel@tonic-gate #endif	/* Etherboot Code */
1187c478bd9Sstevel@tonic-gate 	return 1;
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
free_base_mem_data(void)1217c478bd9Sstevel@tonic-gate int free_base_mem_data ( void ) {
1227c478bd9Sstevel@tonic-gate 	/* Just pretend to free something :-) */
1237c478bd9Sstevel@tonic-gate 	undi.base_mem_data = NULL;
1247c478bd9Sstevel@tonic-gate 	undi.undi_call_info = NULL;
1257c478bd9Sstevel@tonic-gate 	undi.pxs = NULL;
1267c478bd9Sstevel@tonic-gate 	undi.xmit_data = NULL;
1277c478bd9Sstevel@tonic-gate 	undi.xmit_buffer = NULL;
1287c478bd9Sstevel@tonic-gate #if 0				/* Etherboot Code */
1297c478bd9Sstevel@tonic-gate 	if ( undi.base_mem_data != NULL ) {
1307c478bd9Sstevel@tonic-gate 		forget_base_memory ( undi.base_mem_data,
1317c478bd9Sstevel@tonic-gate 				     sizeof(undi_base_mem_data_t) +
1327c478bd9Sstevel@tonic-gate 				     TRIVIAL_IRQ_HANDLER_SIZE );
1337c478bd9Sstevel@tonic-gate 		undi.base_mem_data = NULL;
1347c478bd9Sstevel@tonic-gate 		undi.undi_call_info = NULL;
1357c478bd9Sstevel@tonic-gate 		undi.pxs = NULL;
1367c478bd9Sstevel@tonic-gate 		undi.xmit_data = NULL;
1377c478bd9Sstevel@tonic-gate 		undi.xmit_buffer = NULL;
1387c478bd9Sstevel@tonic-gate 		copy_trivial_irq_handler ( NULL, 0 );
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate #endif	/* Etherboot Code */
1417c478bd9Sstevel@tonic-gate 	return 1;
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate 
assemble_firing_squad(firing_squad_lineup_t * lineup,void * start,size_t size,firing_squad_shoot_t shoot)1447c478bd9Sstevel@tonic-gate void assemble_firing_squad ( firing_squad_lineup_t *lineup,
1457c478bd9Sstevel@tonic-gate 			     void *start, size_t size,
1467c478bd9Sstevel@tonic-gate 			     firing_squad_shoot_t shoot ) {
1477c478bd9Sstevel@tonic-gate 	int target;
1487c478bd9Sstevel@tonic-gate 	int index;
1497c478bd9Sstevel@tonic-gate 	int bit;
1507c478bd9Sstevel@tonic-gate 	int start_kb = virt_to_phys(start) >> 10;
1517c478bd9Sstevel@tonic-gate 	int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	for ( target = start_kb; target <= end_kb; target++ ) {
1547c478bd9Sstevel@tonic-gate 		index = FIRING_SQUAD_TARGET_INDEX ( target );
1557c478bd9Sstevel@tonic-gate 		bit = FIRING_SQUAD_TARGET_BIT ( target );
1567c478bd9Sstevel@tonic-gate 		lineup->targets[index] = ( shoot << bit ) |
1577c478bd9Sstevel@tonic-gate 			( lineup->targets[index] & ~( 1 << bit ) );
1587c478bd9Sstevel@tonic-gate 	}
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
shoot_targets(firing_squad_lineup_t * lineup)1617c478bd9Sstevel@tonic-gate void shoot_targets ( firing_squad_lineup_t *lineup ) {
1627c478bd9Sstevel@tonic-gate 	int shoot_this_target = 0;
1637c478bd9Sstevel@tonic-gate 	int shoot_last_target = 0;
1647c478bd9Sstevel@tonic-gate 	int start_target = 0;
1657c478bd9Sstevel@tonic-gate 	int target;
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	for ( target = 0; target <= 640; target++ ) {
1687c478bd9Sstevel@tonic-gate 		shoot_this_target = ( target == 640 ? 0 :
1697c478bd9Sstevel@tonic-gate 		      ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) &
1707c478bd9Sstevel@tonic-gate 		      lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] );
1717c478bd9Sstevel@tonic-gate 		if ( shoot_this_target && !shoot_last_target ) {
1727c478bd9Sstevel@tonic-gate 			start_target = target;
1737c478bd9Sstevel@tonic-gate 		} else if ( shoot_last_target && !shoot_this_target ) {
1747c478bd9Sstevel@tonic-gate 			size_t range_size = ( target - start_target ) << 10;
1757c478bd9Sstevel@tonic-gate 			forget_base_memory ( phys_to_virt( start_target<<10 ),
1767c478bd9Sstevel@tonic-gate 					     range_size );
1777c478bd9Sstevel@tonic-gate 		}
1787c478bd9Sstevel@tonic-gate 		shoot_last_target = shoot_this_target;
1797c478bd9Sstevel@tonic-gate 	}
1807c478bd9Sstevel@tonic-gate }
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate /* Debug macros
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate #ifdef TRACE_UNDI
1867c478bd9Sstevel@tonic-gate #define DBG(...) printf ( __VA_ARGS__ )
1877c478bd9Sstevel@tonic-gate #else
1887c478bd9Sstevel@tonic-gate #define DBG(...)
1897c478bd9Sstevel@tonic-gate #endif
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate #define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
1927c478bd9Sstevel@tonic-gate 			      "SUCCESS" : \
1937c478bd9Sstevel@tonic-gate 			      ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
1947c478bd9Sstevel@tonic-gate 				"FAILURE" : "UNKNOWN" ) )
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate /**************************************************************************
1977c478bd9Sstevel@tonic-gate  * Base memory scanning functions
1987c478bd9Sstevel@tonic-gate  **************************************************************************/
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /* Locate the $PnP structure indicating a PnP BIOS.
2017c478bd9Sstevel@tonic-gate  */
2027c478bd9Sstevel@tonic-gate 
hunt_pnp_bios(void)2037c478bd9Sstevel@tonic-gate int hunt_pnp_bios ( void ) {
2047c478bd9Sstevel@tonic-gate 	uint32_t off = 0x10000;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	DBG ( "Hunting for PnP BIOS..." );
2077c478bd9Sstevel@tonic-gate 	while ( off > 0 ) {
2087c478bd9Sstevel@tonic-gate 		off -= 16;
2097c478bd9Sstevel@tonic-gate 		undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off );
2107c478bd9Sstevel@tonic-gate 		if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) {
2117c478bd9Sstevel@tonic-gate 			DBG ( "found $PnP at f000:%hx...", off );
2127c478bd9Sstevel@tonic-gate 			if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) {
2137c478bd9Sstevel@tonic-gate 				DBG ( "invalid checksum\n..." );
2147c478bd9Sstevel@tonic-gate 				continue;
2157c478bd9Sstevel@tonic-gate 			}
2167c478bd9Sstevel@tonic-gate 			DBG ( "ok\n" );
2177c478bd9Sstevel@tonic-gate 			return 1;
2187c478bd9Sstevel@tonic-gate 		}
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 	DBG ( "none found\n" );
2217c478bd9Sstevel@tonic-gate 	undi.pnp_bios = NULL;
2227c478bd9Sstevel@tonic-gate 	return 0;
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /* Locate the !PXE structure indicating a loaded UNDI driver.
2267c478bd9Sstevel@tonic-gate  */
2277c478bd9Sstevel@tonic-gate 
hunt_pixie(void)2287c478bd9Sstevel@tonic-gate int hunt_pixie ( void ) {
2297c478bd9Sstevel@tonic-gate 	static uint32_t ptr = 0;
2307c478bd9Sstevel@tonic-gate 	pxe_t *pxe = NULL;
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	DBG ( "Hunting for pixies..." );
2337c478bd9Sstevel@tonic-gate 	if ( ptr == 0 ) ptr = 0xa0000;
2347c478bd9Sstevel@tonic-gate 	while ( ptr > 0x10000 ) {
2357c478bd9Sstevel@tonic-gate 		ptr -= 16;
2367c478bd9Sstevel@tonic-gate 		pxe = (pxe_t *) phys_to_virt ( ptr );
2377c478bd9Sstevel@tonic-gate 		if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) {
2387c478bd9Sstevel@tonic-gate 			DBG ( "found !PXE at %x...", ptr );
2397c478bd9Sstevel@tonic-gate 			if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
2407c478bd9Sstevel@tonic-gate 				DBG ( "invalid checksum\n..." );
2417c478bd9Sstevel@tonic-gate 				continue;
2427c478bd9Sstevel@tonic-gate 			}
2437c478bd9Sstevel@tonic-gate 			if ( ptr < get_free_base_memory() ) {
2447c478bd9Sstevel@tonic-gate 				DBG ( "in free base memory!\n\n"
2457c478bd9Sstevel@tonic-gate 					 "WARNING: a valid !PXE structure was "
2467c478bd9Sstevel@tonic-gate 					 "found in an area of memory marked "
2477c478bd9Sstevel@tonic-gate 					 "as free!\n\n" );
2487c478bd9Sstevel@tonic-gate 				undi.pxe = pxe;
2497c478bd9Sstevel@tonic-gate 				pxe_dump();
2507c478bd9Sstevel@tonic-gate 				undi.pxe = NULL;
2517c478bd9Sstevel@tonic-gate 				DBG ( "\nIgnoring and continuing, but this "
2527c478bd9Sstevel@tonic-gate 					 "may cause problems later!\n\n" );
2537c478bd9Sstevel@tonic-gate 				continue;
2547c478bd9Sstevel@tonic-gate 			}
2557c478bd9Sstevel@tonic-gate 			DBG ( "ok\n" );
2567c478bd9Sstevel@tonic-gate 			undi.pxe = pxe;
2577c478bd9Sstevel@tonic-gate 			pxe_dump();
2587c478bd9Sstevel@tonic-gate 			DBG ( "Resetting pixie...\n" );
2597c478bd9Sstevel@tonic-gate 			undi_unload_base_code();
2607c478bd9Sstevel@tonic-gate 			eb_pxenv_stop_undi();
2617c478bd9Sstevel@tonic-gate 			pxe_dump();
2627c478bd9Sstevel@tonic-gate 			return 1;
2637c478bd9Sstevel@tonic-gate 		}
2647c478bd9Sstevel@tonic-gate 	}
2657c478bd9Sstevel@tonic-gate 	DBG ( "none found\n" );
2667c478bd9Sstevel@tonic-gate 	ptr = 0;
2677c478bd9Sstevel@tonic-gate 	return 0;
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /* Locate PCI PnP ROMs.
2717c478bd9Sstevel@tonic-gate  */
2727c478bd9Sstevel@tonic-gate 
hunt_rom(void)2737c478bd9Sstevel@tonic-gate int hunt_rom ( void ) {
2747c478bd9Sstevel@tonic-gate 	static uint32_t ptr = 0;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	DBG ( "Hunting for ROMs..." );
2777c478bd9Sstevel@tonic-gate 	if ( ptr == 0 ) ptr = 0x100000;
2787c478bd9Sstevel@tonic-gate 	while ( ptr > 0x0c0000 ) {
2797c478bd9Sstevel@tonic-gate 		ptr -= 0x800;
2807c478bd9Sstevel@tonic-gate 		undi.rom = ( rom_t * ) phys_to_virt ( ptr );
2817c478bd9Sstevel@tonic-gate 		if ( undi.rom->signature == ROM_SIGNATURE ) {
2827c478bd9Sstevel@tonic-gate 			pcir_header_t *pcir_header = NULL;
2837c478bd9Sstevel@tonic-gate 			pnp_header_t *pnp_header = NULL;
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 			DBG ( "found 55AA at %x...", ptr );
2867c478bd9Sstevel@tonic-gate 			if ( undi.rom->pcir_off == 0 ) {
2877c478bd9Sstevel@tonic-gate 				DBG ( "not a PCI ROM\n..." );
2887c478bd9Sstevel@tonic-gate 				continue;
2897c478bd9Sstevel@tonic-gate 			}
2907c478bd9Sstevel@tonic-gate 			pcir_header = (pcir_header_t*)( ( void * ) undi.rom +
2917c478bd9Sstevel@tonic-gate 							undi.rom->pcir_off );
2927c478bd9Sstevel@tonic-gate 			if ( pcir_header->signature != PCIR_SIGNATURE ) {
2937c478bd9Sstevel@tonic-gate 				DBG ( "invalid PCI signature\n..." );
2947c478bd9Sstevel@tonic-gate 				continue;
2957c478bd9Sstevel@tonic-gate 			}
2967c478bd9Sstevel@tonic-gate 			DBG ( "PCI:%hx:%hx...", pcir_header->vendor_id,
2977c478bd9Sstevel@tonic-gate 				 pcir_header->device_id );
2987c478bd9Sstevel@tonic-gate 			if ( ( pcir_header->vendor_id != undi.pci.vendor ) ||
2997c478bd9Sstevel@tonic-gate 			     ( pcir_header->device_id != undi.pci.dev_id ) ) {
3007c478bd9Sstevel@tonic-gate 				DBG ( "not me (%hx:%hx)\n...",
3017c478bd9Sstevel@tonic-gate 					 undi.pci.vendor,
3027c478bd9Sstevel@tonic-gate 					 undi.pci.dev_id );
3037c478bd9Sstevel@tonic-gate 				continue;
3047c478bd9Sstevel@tonic-gate 			}
3057c478bd9Sstevel@tonic-gate 			if ( undi.rom->pnp_off == 0 ) {
3067c478bd9Sstevel@tonic-gate 				DBG ( "not a PnP ROM\n..." );
3077c478bd9Sstevel@tonic-gate 				continue;
3087c478bd9Sstevel@tonic-gate 			}
3097c478bd9Sstevel@tonic-gate 			pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
3107c478bd9Sstevel@tonic-gate 							 undi.rom->pnp_off );
3117c478bd9Sstevel@tonic-gate 			if ( pnp_header->signature != PNP_SIGNATURE ) {
3127c478bd9Sstevel@tonic-gate 				DBG ( "invalid $PnP signature\n..." );
3137c478bd9Sstevel@tonic-gate 				continue;
3147c478bd9Sstevel@tonic-gate 			}
3157c478bd9Sstevel@tonic-gate 			if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
3167c478bd9Sstevel@tonic-gate 				DBG ( "invalid PnP checksum\n..." );
3177c478bd9Sstevel@tonic-gate 				continue;
3187c478bd9Sstevel@tonic-gate 			}
3197c478bd9Sstevel@tonic-gate 			DBG ( "ok\n");
3207c478bd9Sstevel@tonic-gate 			printf ("ROM %s by %s\n",
3217c478bd9Sstevel@tonic-gate 				 pnp_header->product_str_off==0 ? "(unknown)" :
3227c478bd9Sstevel@tonic-gate 				 (void*)undi.rom+pnp_header->product_str_off,
3237c478bd9Sstevel@tonic-gate 				 pnp_header->manuf_str_off==0 ? "(unknown)" :
3247c478bd9Sstevel@tonic-gate 				 (void*)undi.rom+pnp_header->manuf_str_off );
3257c478bd9Sstevel@tonic-gate 			return 1;
3267c478bd9Sstevel@tonic-gate 		}
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate 	DBG ( "none found\n" );
3297c478bd9Sstevel@tonic-gate 	ptr = 0;
3307c478bd9Sstevel@tonic-gate 	undi.rom = NULL;
3317c478bd9Sstevel@tonic-gate 	return 0;
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate /* Locate ROMs containing UNDI drivers.
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate 
hunt_undi_rom(void)3377c478bd9Sstevel@tonic-gate int hunt_undi_rom ( void ) {
3387c478bd9Sstevel@tonic-gate 	while ( hunt_rom() ) {
3397c478bd9Sstevel@tonic-gate 		if ( undi.rom->undi_rom_id_off == 0 ) {
3407c478bd9Sstevel@tonic-gate 			DBG ( "Not a PXE ROM\n" );
3417c478bd9Sstevel@tonic-gate 			continue;
3427c478bd9Sstevel@tonic-gate 		}
3437c478bd9Sstevel@tonic-gate 		undi.undi_rom_id = (undi_rom_id_t *)
3447c478bd9Sstevel@tonic-gate 			( (void *)undi.rom + undi.rom->undi_rom_id_off );
3457c478bd9Sstevel@tonic-gate 		if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) {
3467c478bd9Sstevel@tonic-gate 			DBG ( "Invalid UNDI signature\n" );
3477c478bd9Sstevel@tonic-gate 			continue;
3487c478bd9Sstevel@tonic-gate 		}
3497c478bd9Sstevel@tonic-gate 		printf ( "Revision %d.%d.%d",
3507c478bd9Sstevel@tonic-gate 			 undi.undi_rom_id->undi_rev[2],
3517c478bd9Sstevel@tonic-gate 			 undi.undi_rom_id->undi_rev[1],
3527c478bd9Sstevel@tonic-gate 			 undi.undi_rom_id->undi_rev[0] );
3537c478bd9Sstevel@tonic-gate 		return 1;
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 	return 0;
3567c478bd9Sstevel@tonic-gate }
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate /**************************************************************************
3597c478bd9Sstevel@tonic-gate  * Low-level UNDI API call wrappers
3607c478bd9Sstevel@tonic-gate  **************************************************************************/
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate /* Make a real-mode UNDI API call to the UNDI routine at
3637c478bd9Sstevel@tonic-gate  * routine_seg:routine_off, passing in three uint16 parameters on the
3647c478bd9Sstevel@tonic-gate  * real-mode stack.
3657c478bd9Sstevel@tonic-gate  * Calls the assembler wrapper routine __undi_call.
3667c478bd9Sstevel@tonic-gate  */
3677c478bd9Sstevel@tonic-gate 
_undi_call(uint16_t routine_seg,uint16_t routine_off,uint16_t st0,uint16_t st1,uint16_t st2)3687c478bd9Sstevel@tonic-gate static inline PXENV_EXIT_t _undi_call ( uint16_t routine_seg,
3697c478bd9Sstevel@tonic-gate 					uint16_t routine_off, uint16_t st0,
3707c478bd9Sstevel@tonic-gate 					uint16_t st1, uint16_t st2 ) {
3717c478bd9Sstevel@tonic-gate 	PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	undi.undi_call_info->routine.segment = routine_seg;
3747c478bd9Sstevel@tonic-gate 	undi.undi_call_info->routine.offset = routine_off;
3757c478bd9Sstevel@tonic-gate 	undi.undi_call_info->stack[0] = st0;
3767c478bd9Sstevel@tonic-gate 	undi.undi_call_info->stack[1] = st1;
3777c478bd9Sstevel@tonic-gate 	undi.undi_call_info->stack[2] = st2;
3787c478bd9Sstevel@tonic-gate 	ret = __undi_call ( SEGMENT( undi.undi_call_info ),
3797c478bd9Sstevel@tonic-gate 			    OFFSET( undi.undi_call_info ) );
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/* UNDI API calls may rudely change the status of A20 and not
3827c478bd9Sstevel@tonic-gate 	 * bother to restore it afterwards.  Intel is known to be
3837c478bd9Sstevel@tonic-gate 	 * guilty of this.
3847c478bd9Sstevel@tonic-gate 	 *
3857c478bd9Sstevel@tonic-gate 	 * Note that we will return to this point even if A20 gets
3867c478bd9Sstevel@tonic-gate 	 * screwed up by the UNDI driver, because Etherboot always
3877c478bd9Sstevel@tonic-gate 	 * resides in an even megabyte of RAM.
3887c478bd9Sstevel@tonic-gate 	 */
3897c478bd9Sstevel@tonic-gate 	gateA20_set();
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	return ret;
3927c478bd9Sstevel@tonic-gate }
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate /* Make a real-mode call to the UNDI loader routine at
3957c478bd9Sstevel@tonic-gate  * routine_seg:routine_off, passing in the seg:off address of a
3967c478bd9Sstevel@tonic-gate  * pxenv_structure on the real-mode stack.
3977c478bd9Sstevel@tonic-gate  */
3987c478bd9Sstevel@tonic-gate 
undi_call_loader(void)3997c478bd9Sstevel@tonic-gate int undi_call_loader ( void ) {
4007c478bd9Sstevel@tonic-gate 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
4037c478bd9Sstevel@tonic-gate 				  undi.undi_rom_id->undi_loader_off,
4047c478bd9Sstevel@tonic-gate 				  OFFSET( undi.pxs ),
4057c478bd9Sstevel@tonic-gate 				  SEGMENT( undi.pxs ),
4067c478bd9Sstevel@tonic-gate 				  0 /* Unused for UNDI loader API */ );
4077c478bd9Sstevel@tonic-gate 	/* Return 1 for success, to be consistent with other routines */
4087c478bd9Sstevel@tonic-gate 	if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1;
4097c478bd9Sstevel@tonic-gate 	DBG ( "UNDI loader call failed with status %#hx\n",
4107c478bd9Sstevel@tonic-gate 		 undi.pxs->Status );
4117c478bd9Sstevel@tonic-gate 	return 0;
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate /* Make a real-mode UNDI API call, passing in the opcode and the
4157c478bd9Sstevel@tonic-gate  * seg:off address of a pxenv_structure on the real-mode stack.
4167c478bd9Sstevel@tonic-gate  *
4177c478bd9Sstevel@tonic-gate  * Two versions: undi_call() will automatically report any failure
4187c478bd9Sstevel@tonic-gate  * codes, undi_call_silent() will not.
4197c478bd9Sstevel@tonic-gate  */
4207c478bd9Sstevel@tonic-gate 
undi_call_silent(uint16_t opcode)4217c478bd9Sstevel@tonic-gate int undi_call_silent ( uint16_t opcode ) {
4227c478bd9Sstevel@tonic-gate 	PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
4257c478bd9Sstevel@tonic-gate 				  undi.pxe->EntryPointSP.offset,
4267c478bd9Sstevel@tonic-gate 				  opcode,
4277c478bd9Sstevel@tonic-gate 				  OFFSET( undi.pxs ),
4287c478bd9Sstevel@tonic-gate 				  SEGMENT( undi.pxs ) );
4297c478bd9Sstevel@tonic-gate 	/* Return 1 for success, to be consistent with other routines */
4307c478bd9Sstevel@tonic-gate 	return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
undi_call(uint16_t opcode)4337c478bd9Sstevel@tonic-gate int undi_call ( uint16_t opcode ) {
4347c478bd9Sstevel@tonic-gate 	if ( undi_call_silent ( opcode ) ) return 1;
4357c478bd9Sstevel@tonic-gate 	DBG ( "UNDI API call %#hx failed with status %#hx\n",
4367c478bd9Sstevel@tonic-gate 		 opcode, undi.pxs->Status );
4377c478bd9Sstevel@tonic-gate 	return 0;
4387c478bd9Sstevel@tonic-gate }
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate /**************************************************************************
4417c478bd9Sstevel@tonic-gate  * High-level UNDI API call wrappers
4427c478bd9Sstevel@tonic-gate  **************************************************************************/
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate /* Install the UNDI driver from a located UNDI ROM.
4457c478bd9Sstevel@tonic-gate  */
4467c478bd9Sstevel@tonic-gate 
undi_loader(void)4477c478bd9Sstevel@tonic-gate int undi_loader ( void ) {
4487c478bd9Sstevel@tonic-gate 	pxe_t *pxe = NULL;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	/* AX contains PCI bus:devfn (PCI specification) */
4517c478bd9Sstevel@tonic-gate 	undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
4527c478bd9Sstevel@tonic-gate 	/* BX and DX set to 0xffff for non-ISAPnP devices
4537c478bd9Sstevel@tonic-gate 	 * (BIOS boot specification)
4547c478bd9Sstevel@tonic-gate 	 */
4557c478bd9Sstevel@tonic-gate 	undi.pxs->loader.bx = 0xffff;
4567c478bd9Sstevel@tonic-gate 	undi.pxs->loader.dx = 0xffff;
4577c478bd9Sstevel@tonic-gate 	/* ES:DI points to PnP BIOS' $PnP structure
4587c478bd9Sstevel@tonic-gate 	 * (BIOS boot specification)
4597c478bd9Sstevel@tonic-gate 	 */
4607c478bd9Sstevel@tonic-gate 	undi.pxs->loader.es = 0xf000;
4617c478bd9Sstevel@tonic-gate 	undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	/* Allocate space for UNDI driver's code and data segments */
4647c478bd9Sstevel@tonic-gate 	undi.driver_code_size = undi.undi_rom_id->code_size;
4657c478bd9Sstevel@tonic-gate 	undi.driver_code = UNDI_HEAP;
4667c478bd9Sstevel@tonic-gate 	if ( undi.driver_code == NULL ) {
4677c478bd9Sstevel@tonic-gate 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
4687c478bd9Sstevel@tonic-gate 			 undi.driver_code_size );
4697c478bd9Sstevel@tonic-gate 		return 0;
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 	undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	undi.driver_data_size = undi.undi_rom_id->data_size;
4747c478bd9Sstevel@tonic-gate 	undi.driver_data = (void *)((((unsigned long)UNDI_HEAP + undi.undi_rom_id->code_size) | (1024 -1)) + 1);
4757c478bd9Sstevel@tonic-gate 	if ( undi.driver_data == NULL ) {
4767c478bd9Sstevel@tonic-gate 		printf ( "Could not allocate %d bytes for UNDI code segment\n",
4777c478bd9Sstevel@tonic-gate 			 undi.driver_data_size );
4787c478bd9Sstevel@tonic-gate 		return 0;
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 	undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	DBG ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
4837c478bd9Sstevel@tonic-gate 		undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds );
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 	/* Do the API call to install the loader */
4867c478bd9Sstevel@tonic-gate 	if ( ! undi_call_loader () ) return 0;
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	pxe = VIRTUAL( undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
4897c478bd9Sstevel@tonic-gate 	DBG ( "UNDI driver created a pixie at %hx:%hx...",
4907c478bd9Sstevel@tonic-gate 		 undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_off );
4917c478bd9Sstevel@tonic-gate 	if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) {
4927c478bd9Sstevel@tonic-gate 		DBG ( "invalid signature\n" );
4937c478bd9Sstevel@tonic-gate 		return 0;
4947c478bd9Sstevel@tonic-gate 	}
4957c478bd9Sstevel@tonic-gate 	if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
4967c478bd9Sstevel@tonic-gate 		DBG ( "invalid checksum\n" );
4977c478bd9Sstevel@tonic-gate 		return 0;
4987c478bd9Sstevel@tonic-gate 	}
4997c478bd9Sstevel@tonic-gate 	DBG ( "ok\n" );
5007c478bd9Sstevel@tonic-gate 	undi.pxe = pxe;
5017c478bd9Sstevel@tonic-gate 	pxe_dump();
5027c478bd9Sstevel@tonic-gate 	return 1;
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate /* Start the UNDI driver.
5067c478bd9Sstevel@tonic-gate  */
5077c478bd9Sstevel@tonic-gate 
eb_pxenv_start_undi(void)5087c478bd9Sstevel@tonic-gate int eb_pxenv_start_undi ( void ) {
5097c478bd9Sstevel@tonic-gate 	int success = 0;
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	/* AX contains PCI bus:devfn (PCI specification) */
5127c478bd9Sstevel@tonic-gate 	undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
5137c478bd9Sstevel@tonic-gate 	/* BX and DX set to 0xffff for non-ISAPnP devices
5147c478bd9Sstevel@tonic-gate 	 * (BIOS boot specification)
5157c478bd9Sstevel@tonic-gate 	 */
5167c478bd9Sstevel@tonic-gate 	undi.pxs->start_undi.bx = 0xffff;
5177c478bd9Sstevel@tonic-gate 	undi.pxs->start_undi.dx = 0xffff;
5187c478bd9Sstevel@tonic-gate 	/* ES:DI points to PnP BIOS' $PnP structure
5197c478bd9Sstevel@tonic-gate 	 * (BIOS boot specification)
5207c478bd9Sstevel@tonic-gate 	 */
5217c478bd9Sstevel@tonic-gate 	undi.pxs->start_undi.es = 0xf000;
5227c478bd9Sstevel@tonic-gate 	undi.pxs->start_undi.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
5257c478bd9Sstevel@tonic-gate 	      undi.pxs->start_undi.ax,
5267c478bd9Sstevel@tonic-gate 	      undi.pxs->start_undi.bx, undi.pxs->start_undi.dx,
5277c478bd9Sstevel@tonic-gate 	      undi.pxs->start_undi.es, undi.pxs->start_undi.di );
5287c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_START_UNDI );
5297c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5307c478bd9Sstevel@tonic-gate 	if ( success ) undi.prestarted = 1;
5317c478bd9Sstevel@tonic-gate 	return success;
5327c478bd9Sstevel@tonic-gate }
5337c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_startup(void)5347c478bd9Sstevel@tonic-gate int eb_pxenv_undi_startup ( void )	{
5357c478bd9Sstevel@tonic-gate 	int success = 0;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
5387c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_STARTUP );
5397c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5407c478bd9Sstevel@tonic-gate 	if ( success ) undi.started = 1;
5417c478bd9Sstevel@tonic-gate 	return success;
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_cleanup(void)5447c478bd9Sstevel@tonic-gate int eb_pxenv_undi_cleanup ( void ) {
5457c478bd9Sstevel@tonic-gate 	int success = 0;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
5487c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_CLEANUP );
5497c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5507c478bd9Sstevel@tonic-gate 	return success;
5517c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_initialize(void)5537c478bd9Sstevel@tonic-gate int eb_pxenv_undi_initialize ( void ) {
5547c478bd9Sstevel@tonic-gate 	int success = 0;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	undi.pxs->undi_initialize.ProtocolIni = 0;
5577c478bd9Sstevel@tonic-gate 	memset ( &undi.pxs->undi_initialize.reserved, 0,
5587c478bd9Sstevel@tonic-gate 		 sizeof ( undi.pxs->undi_initialize.reserved ) );
5597c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
5607c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_INITIALIZE );
5617c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5627c478bd9Sstevel@tonic-gate 	if ( success ) undi.initialized = 1;
5637c478bd9Sstevel@tonic-gate 	return success;
5647c478bd9Sstevel@tonic-gate }
5657c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_shutdown(void)5667c478bd9Sstevel@tonic-gate int eb_pxenv_undi_shutdown ( void ) {
5677c478bd9Sstevel@tonic-gate 	int success = 0;
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
5707c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_SHUTDOWN );
5717c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5727c478bd9Sstevel@tonic-gate 	if ( success ) {
5737c478bd9Sstevel@tonic-gate 		undi.initialized = 0;
5747c478bd9Sstevel@tonic-gate 		undi.started = 0;
5757c478bd9Sstevel@tonic-gate 	}
5767c478bd9Sstevel@tonic-gate 	return success;
5777c478bd9Sstevel@tonic-gate }
5787c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_open(void)5797c478bd9Sstevel@tonic-gate int eb_pxenv_undi_open ( void ) {
5807c478bd9Sstevel@tonic-gate 	int success = 0;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	undi.pxs->undi_open.OpenFlag = 0;
5837c478bd9Sstevel@tonic-gate 	undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	/* Multicast support not yet implemented */
5867c478bd9Sstevel@tonic-gate 	undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0;
5877c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
5887c478bd9Sstevel@tonic-gate 	      "MCastAddrCount=%hx\n",
5897c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter,
5907c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount );
5917c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_OPEN );
5927c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
5937c478bd9Sstevel@tonic-gate 	if ( success ) undi.opened = 1;
5947c478bd9Sstevel@tonic-gate 	return success;
5957c478bd9Sstevel@tonic-gate }
5967c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_close(void)5977c478bd9Sstevel@tonic-gate int eb_pxenv_undi_close ( void ) {
5987c478bd9Sstevel@tonic-gate 	int success = 0;
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
6017c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_CLOSE );
6027c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
6037c478bd9Sstevel@tonic-gate 	if ( success ) undi.opened = 0;
6047c478bd9Sstevel@tonic-gate 	return success;
6057c478bd9Sstevel@tonic-gate }
6067c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_transmit_packet(void)6077c478bd9Sstevel@tonic-gate int eb_pxenv_undi_transmit_packet ( void ) {
6087c478bd9Sstevel@tonic-gate 	int success = 0;
6097c478bd9Sstevel@tonic-gate 	static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	/* XMitFlag selects unicast / broadcast */
6127c478bd9Sstevel@tonic-gate 	if ( memcmp ( undi.xmit_data->destaddr, broadcast,
6137c478bd9Sstevel@tonic-gate 		      sizeof(broadcast) ) == 0 ) {
6147c478bd9Sstevel@tonic-gate 		undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST;
6157c478bd9Sstevel@tonic-gate 	} else {
6167c478bd9Sstevel@tonic-gate 		undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	/* Zero reserved dwords */
6207c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.Reserved[0] = 0;
6217c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.Reserved[1] = 0;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/* Segment:offset pointer to DestAddr in base memory */
6247c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.DestAddr.segment =
6257c478bd9Sstevel@tonic-gate 		SEGMENT( undi.xmit_data->destaddr );
6267c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.DestAddr.offset =
6277c478bd9Sstevel@tonic-gate 		OFFSET( undi.xmit_data->destaddr );
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	/* Segment:offset pointer to TBD in base memory */
6307c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd );
6317c478bd9Sstevel@tonic-gate 	undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd );
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	/* Use only the "immediate" part of the TBD */
6347c478bd9Sstevel@tonic-gate 	undi.xmit_data->tbd.DataBlkCount = 0;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
6377c478bd9Sstevel@tonic-gate 	      "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
6387c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.Protocol,
6397c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.XmitFlag,
6407c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.DestAddr.segment,
6417c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.DestAddr.offset,
6427c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.TBD.segment,
6437c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_transmit.TBD.offset );
6447c478bd9Sstevel@tonic-gate 	DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
6457c478bd9Sstevel@tonic-gate 	      undi.xmit_data->tbd.ImmedLength,
6467c478bd9Sstevel@tonic-gate 	      undi.xmit_data->tbd.Xmit.segment,
6477c478bd9Sstevel@tonic-gate 	      undi.xmit_data->tbd.Xmit.offset,
6487c478bd9Sstevel@tonic-gate 	      undi.xmit_data->tbd.DataBlkCount );
6497c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_TRANSMIT );
6507c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
6517c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs) );
6527c478bd9Sstevel@tonic-gate 	return success;
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_set_station_address(void)6557c478bd9Sstevel@tonic-gate int eb_pxenv_undi_set_station_address ( void ) {
6567c478bd9Sstevel@tonic-gate 	/* This will spuriously fail on some cards.  Ignore failures.
6577c478bd9Sstevel@tonic-gate 	 * We only ever use it to set the MAC address to the card's
6587c478bd9Sstevel@tonic-gate 	 * permanent value anyway, so it's a useless call (although we
6597c478bd9Sstevel@tonic-gate 	 * make it because PXE spec says we should).
6607c478bd9Sstevel@tonic-gate 	 */
6617c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
6627c478bd9Sstevel@tonic-gate 	      "StationAddress=%!\n",
6637c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_set_station_address.StationAddress );
6647c478bd9Sstevel@tonic-gate 	undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS );
6657c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
6667c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs) );
6677c478bd9Sstevel@tonic-gate 	return 1;
6687c478bd9Sstevel@tonic-gate }
6697c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_get_information(void)6707c478bd9Sstevel@tonic-gate int eb_pxenv_undi_get_information ( void ) {
6717c478bd9Sstevel@tonic-gate 	int success = 0;
6727c478bd9Sstevel@tonic-gate 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
6737c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
6747c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_GET_INFORMATION );
6757c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
6767c478bd9Sstevel@tonic-gate 	      "BaseIO=%hx IntNumber=%hx ...\n"
6777c478bd9Sstevel@tonic-gate 	      "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
6787c478bd9Sstevel@tonic-gate 	      "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
6797c478bd9Sstevel@tonic-gate 	      "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
6807c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs),
6817c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.BaseIo,
6827c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.IntNumber,
6837c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.MaxTranUnit,
6847c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.HwType,
6857c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.HwAddrLen,
6867c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.CurrentNodeAddress,
6877c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.PermNodeAddress,
6887c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.ROMAddress,
6897c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.RxBufCt,
6907c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_information.TxBufCt );
6917c478bd9Sstevel@tonic-gate 	return success;
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_get_iface_info(void)6947c478bd9Sstevel@tonic-gate int eb_pxenv_undi_get_iface_info ( void ) {
6957c478bd9Sstevel@tonic-gate 	int success = 0;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
6987c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_GET_IFACE_INFO );
6997c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
7007c478bd9Sstevel@tonic-gate 	      "... LinkSpeed=%x ServiceFlags=%x\n",
7017c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs),
7027c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_iface_info.IfaceType,
7037c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_iface_info.LinkSpeed,
7047c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_get_iface_info.ServiceFlags );
7057c478bd9Sstevel@tonic-gate 	return success;
7067c478bd9Sstevel@tonic-gate }
7077c478bd9Sstevel@tonic-gate 
eb_pxenv_undi_isr(void)7087c478bd9Sstevel@tonic-gate int eb_pxenv_undi_isr ( void ) {
7097c478bd9Sstevel@tonic-gate 	int success = 0;
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
7127c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.FuncFlag );
7137c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_UNDI_ISR );
7147c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
7157c478bd9Sstevel@tonic-gate 	      "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
7167c478bd9Sstevel@tonic-gate 	      "ProtType=%hhx ...\n... PktType=%hhx\n",
7177c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag,
7187c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.BufferLength,
7197c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.FrameLength,
7207c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.FrameHeaderLength,
7217c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.Frame.segment,
7227c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.Frame.offset,
7237c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.ProtType,
7247c478bd9Sstevel@tonic-gate 	      undi.pxs->undi_isr.PktType );
7257c478bd9Sstevel@tonic-gate 	return success;
7267c478bd9Sstevel@tonic-gate }
7277c478bd9Sstevel@tonic-gate 
eb_pxenv_stop_undi(void)7287c478bd9Sstevel@tonic-gate int eb_pxenv_stop_undi ( void ) {
7297c478bd9Sstevel@tonic-gate 	int success = 0;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_STOP_UNDI => (void)\n" );
7327c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_STOP_UNDI );
7337c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
7347c478bd9Sstevel@tonic-gate 	if ( success ) undi.prestarted = 0;
7357c478bd9Sstevel@tonic-gate 	return success;
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate 
eb_pxenv_unload_stack(void)7387c478bd9Sstevel@tonic-gate int eb_pxenv_unload_stack ( void ) {
7397c478bd9Sstevel@tonic-gate 	int success = 0;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
7427c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
7437c478bd9Sstevel@tonic-gate 	success = undi_call_silent ( PXENV_UNLOAD_STACK );
7447c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
7457c478bd9Sstevel@tonic-gate 	      UNDI_STATUS(undi.pxs),
7467c478bd9Sstevel@tonic-gate 	      ( undi.pxs->Status == PXENV_STATUS_SUCCESS ?
7477c478bd9Sstevel@tonic-gate 		"base-code is ready to be removed" :
7487c478bd9Sstevel@tonic-gate 		( undi.pxs->Status == PXENV_STATUS_FAILURE ?
7497c478bd9Sstevel@tonic-gate 		  "the size of free base memory has been changed" :
7507c478bd9Sstevel@tonic-gate 		  ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ?
7517c478bd9Sstevel@tonic-gate 		    "the NIC interrupt vector has been changed" :
7527c478bd9Sstevel@tonic-gate 		    "UNEXPECTED STATUS CODE" ) ) ) );
7537c478bd9Sstevel@tonic-gate 	return success;
7547c478bd9Sstevel@tonic-gate }
7557c478bd9Sstevel@tonic-gate 
eb_pxenv_stop_base(void)7567c478bd9Sstevel@tonic-gate int eb_pxenv_stop_base ( void ) {
7577c478bd9Sstevel@tonic-gate 	int success = 0;
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_STOP_BASE => (void)\n" );
7607c478bd9Sstevel@tonic-gate 	success = undi_call ( PXENV_STOP_BASE );
7617c478bd9Sstevel@tonic-gate 	DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
7627c478bd9Sstevel@tonic-gate 	return success;
7637c478bd9Sstevel@tonic-gate }
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate /* Unload UNDI base code (if any present) and free memory.
7667c478bd9Sstevel@tonic-gate  */
undi_unload_base_code(void)7677c478bd9Sstevel@tonic-gate int undi_unload_base_code ( void ) {
7687c478bd9Sstevel@tonic-gate 	/* In GRUB, we do not allocate anything, but we still can call
7697c478bd9Sstevel@tonic-gate 	 * to free the base space */
7707c478bd9Sstevel@tonic-gate 	void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 );
7717c478bd9Sstevel@tonic-gate 	size_t bc_code_size = undi.pxe->BC_Code.Seg_Size;
7727c478bd9Sstevel@tonic-gate 	void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 );
7737c478bd9Sstevel@tonic-gate 	size_t bc_data_size = undi.pxe->BC_Data.Seg_Size;
7747c478bd9Sstevel@tonic-gate 	void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 );
7757c478bd9Sstevel@tonic-gate 	size_t bc_stck_size = undi.pxe->Stack.Seg_Size;
7767c478bd9Sstevel@tonic-gate 	firing_squad_lineup_t lineup;
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	/* Don't unload if there is no base code present */
7797c478bd9Sstevel@tonic-gate 	if ( undi.pxe->BC_Code.Seg_Addr == 0 ) return 1;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	/* Since we never start the base code, the only time we should
7827c478bd9Sstevel@tonic-gate 	 * reach this is if we were loaded via PXE.  There are many
7837c478bd9Sstevel@tonic-gate 	 * different and conflicting versions of the "correct" way to
7847c478bd9Sstevel@tonic-gate 	 * unload the PXE base code, several of which appear within
7857c478bd9Sstevel@tonic-gate 	 * the PXE specification itself.  This one seems to work for
7867c478bd9Sstevel@tonic-gate 	 * our purposes.
7877c478bd9Sstevel@tonic-gate 	 */
7887c478bd9Sstevel@tonic-gate 	eb_pxenv_stop_base();
7897c478bd9Sstevel@tonic-gate 	//eb_pxenv_unload_stack();
7907c478bd9Sstevel@tonic-gate /*	if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) &&
7917c478bd9Sstevel@tonic-gate 	     ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) ) {
7927c478bd9Sstevel@tonic-gate 		printf ( "Could not free memory allocated to PXE base code: "
7937c478bd9Sstevel@tonic-gate 			 "possible memory leak\n" );
7947c478bd9Sstevel@tonic-gate 		return 0;
7957c478bd9Sstevel@tonic-gate 		}*/
7967c478bd9Sstevel@tonic-gate 	/* Free data structures.  Forget what the PXE specification
7977c478bd9Sstevel@tonic-gate 	 * says about how to calculate the new size of base memory;
7987c478bd9Sstevel@tonic-gate 	 * basemem.c takes care of all that for us.  Note that we also
7997c478bd9Sstevel@tonic-gate 	 * have to free the stack (even though PXE spec doesn't say
8007c478bd9Sstevel@tonic-gate 	 * anything about it) because nothing else is going to do so.
8017c478bd9Sstevel@tonic-gate 	 *
8027c478bd9Sstevel@tonic-gate 	 * Structures will almost certainly not be kB-aligned and
8037c478bd9Sstevel@tonic-gate 	 * there's a reasonable chance that the UNDI code or data
8047c478bd9Sstevel@tonic-gate 	 * portions will lie in the same kB as the base code.  Since
8057c478bd9Sstevel@tonic-gate 	 * forget_base_memory works only in 1kB increments, this means
8067c478bd9Sstevel@tonic-gate 	 * we have to do some arcane trickery.
8077c478bd9Sstevel@tonic-gate 	 */
8087c478bd9Sstevel@tonic-gate 	memset ( &lineup, 0, sizeof(lineup) );
8097c478bd9Sstevel@tonic-gate 	if ( SEGMENT(bc_code) != 0 )
8107c478bd9Sstevel@tonic-gate 		assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT );
8117c478bd9Sstevel@tonic-gate 	if ( SEGMENT(bc_data) != 0 )
8127c478bd9Sstevel@tonic-gate 		assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT );
8137c478bd9Sstevel@tonic-gate 	if ( SEGMENT(bc_stck) != 0 )
8147c478bd9Sstevel@tonic-gate 		assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT );
8157c478bd9Sstevel@tonic-gate 	/* Don't shoot any bits of the UNDI driver code or data */
8167c478bd9Sstevel@tonic-gate 	assemble_firing_squad ( &lineup,
8177c478bd9Sstevel@tonic-gate 				VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0),
8187c478bd9Sstevel@tonic-gate 				undi.pxe->UNDICode.Seg_Size, DONTSHOOT );
8197c478bd9Sstevel@tonic-gate 	assemble_firing_squad ( &lineup,
8207c478bd9Sstevel@tonic-gate 				VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0),
8217c478bd9Sstevel@tonic-gate 				undi.pxe->UNDIData.Seg_Size, DONTSHOOT );
8227c478bd9Sstevel@tonic-gate 	//shoot_targets ( &lineup );
8237c478bd9Sstevel@tonic-gate 	//undi.pxe->BC_Code.Seg_Addr = 0;
8247c478bd9Sstevel@tonic-gate 	//undi.pxe->BC_Data.Seg_Addr = 0;
8257c478bd9Sstevel@tonic-gate 	//undi.pxe->Stack.Seg_Addr = 0;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	/* Free and reallocate our own base memory data structures, to
8287c478bd9Sstevel@tonic-gate 	 * allow the freed base-code blocks to be fully released.
8297c478bd9Sstevel@tonic-gate 	 */
8307c478bd9Sstevel@tonic-gate 	free_base_mem_data();
8317c478bd9Sstevel@tonic-gate 	if ( ! allocate_base_mem_data() ) {
8327c478bd9Sstevel@tonic-gate 		printf ( "FATAL: memory unaccountably lost\n" );
8337c478bd9Sstevel@tonic-gate 		while ( 1 ) {};
8347c478bd9Sstevel@tonic-gate 	}
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	return 1;
8377c478bd9Sstevel@tonic-gate }
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate /* UNDI full initialization
8407c478bd9Sstevel@tonic-gate  *
8417c478bd9Sstevel@tonic-gate  * This calls all the various UNDI initialization routines in sequence.
8427c478bd9Sstevel@tonic-gate  */
8437c478bd9Sstevel@tonic-gate 
undi_full_startup(void)8447c478bd9Sstevel@tonic-gate int undi_full_startup ( void ) {
8457c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_start_undi() ) return 0;
8467c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_startup() ) return 0;
8477c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_initialize() ) return 0;
8487c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_get_information() ) return 0;
8497c478bd9Sstevel@tonic-gate 	undi.irq = undi.pxs->undi_get_information.IntNumber;
8507c478bd9Sstevel@tonic-gate 	if ( ! install_undi_irq_handler ( undi.irq, undi.pxe->EntryPointSP ) ) {
8517c478bd9Sstevel@tonic-gate 		undi.irq = IRQ_NONE;
8527c478bd9Sstevel@tonic-gate 		return 0;
8537c478bd9Sstevel@tonic-gate 	}
8547c478bd9Sstevel@tonic-gate 	memmove ( &undi.pxs->undi_set_station_address.StationAddress,
8557c478bd9Sstevel@tonic-gate 		  &undi.pxs->undi_get_information.PermNodeAddress,
8567c478bd9Sstevel@tonic-gate 		  sizeof (undi.pxs->undi_set_station_address.StationAddress) );
8577c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_set_station_address() ) return 0;
8587c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_open() ) return 0;
8597c478bd9Sstevel@tonic-gate 	/* install_undi_irq_handler leaves irq disabled */
8607c478bd9Sstevel@tonic-gate 	enable_irq ( undi.irq );
8617c478bd9Sstevel@tonic-gate 	return 1;
8627c478bd9Sstevel@tonic-gate }
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate /* UNDI full shutdown
8657c478bd9Sstevel@tonic-gate  *
8667c478bd9Sstevel@tonic-gate  * This calls all the various UNDI shutdown routines in sequence and
8677c478bd9Sstevel@tonic-gate  * also frees any memory that it can.
8687c478bd9Sstevel@tonic-gate  */
8697c478bd9Sstevel@tonic-gate 
undi_full_shutdown(void)8707c478bd9Sstevel@tonic-gate int undi_full_shutdown ( void ) {
8717c478bd9Sstevel@tonic-gate 	if ( undi.pxe != NULL ) {
8727c478bd9Sstevel@tonic-gate 		/* In case we didn't allocate the driver's memory in the first
8737c478bd9Sstevel@tonic-gate 		 * place, try to grab the code and data segments and sizes
8747c478bd9Sstevel@tonic-gate 		 * from the !PXE structure.
8757c478bd9Sstevel@tonic-gate 		 */
8767c478bd9Sstevel@tonic-gate 		if ( undi.driver_code == NULL ) {
8777c478bd9Sstevel@tonic-gate 			undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
8787c478bd9Sstevel@tonic-gate 						   0 );
8797c478bd9Sstevel@tonic-gate 			undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
8807c478bd9Sstevel@tonic-gate 		}
8817c478bd9Sstevel@tonic-gate 		if ( undi.driver_data == NULL ) {
8827c478bd9Sstevel@tonic-gate 			undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
8837c478bd9Sstevel@tonic-gate 						   0 );
8847c478bd9Sstevel@tonic-gate 			undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 		/* Ignore errors and continue in the hope of shutting
8887c478bd9Sstevel@tonic-gate 		 * down anyway
8897c478bd9Sstevel@tonic-gate 		 */
8907c478bd9Sstevel@tonic-gate 		if ( undi.opened ) eb_pxenv_undi_close();
8917c478bd9Sstevel@tonic-gate 		if ( undi.started ) {
8927c478bd9Sstevel@tonic-gate 			eb_pxenv_undi_cleanup();
8937c478bd9Sstevel@tonic-gate 			/* We may get spurious UNDI API errors at this
8947c478bd9Sstevel@tonic-gate 			 * point.  If startup() succeeded but
8957c478bd9Sstevel@tonic-gate 			 * initialize() failed then according to the
8967c478bd9Sstevel@tonic-gate 			 * spec, we should call shutdown().  However,
8977c478bd9Sstevel@tonic-gate 			 * some NICS will fail with a status code
8987c478bd9Sstevel@tonic-gate 			 * 0x006a (INVALID_STATE).
8997c478bd9Sstevel@tonic-gate 			 */
9007c478bd9Sstevel@tonic-gate 			eb_pxenv_undi_shutdown();
9017c478bd9Sstevel@tonic-gate 		}
9027c478bd9Sstevel@tonic-gate 		if ( undi.irq != IRQ_NONE ) {
9037c478bd9Sstevel@tonic-gate 			remove_undi_irq_handler ( undi.irq );
9047c478bd9Sstevel@tonic-gate 			undi.irq = IRQ_NONE;
9057c478bd9Sstevel@tonic-gate 		}
9067c478bd9Sstevel@tonic-gate 		undi_unload_base_code();
9077c478bd9Sstevel@tonic-gate 		if ( undi.prestarted ) {
9087c478bd9Sstevel@tonic-gate 			eb_pxenv_stop_undi();
9097c478bd9Sstevel@tonic-gate 			/* Success OR Failure indicates that memory
9107c478bd9Sstevel@tonic-gate 			 * can be freed.  Any other status code means
9117c478bd9Sstevel@tonic-gate 			 * that it can't.
9127c478bd9Sstevel@tonic-gate 			 */
9137c478bd9Sstevel@tonic-gate 			if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) ||
9147c478bd9Sstevel@tonic-gate 			    ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) {
9157c478bd9Sstevel@tonic-gate 				printf ("Could not free memory allocated to "
9167c478bd9Sstevel@tonic-gate 					"UNDI driver: possible memory leak\n");
9177c478bd9Sstevel@tonic-gate 				return 0;
9187c478bd9Sstevel@tonic-gate 			}
9197c478bd9Sstevel@tonic-gate 		}
9207c478bd9Sstevel@tonic-gate 	}
9217c478bd9Sstevel@tonic-gate 	/* Free memory allocated to UNDI driver */
9227c478bd9Sstevel@tonic-gate 	if ( undi.driver_code != NULL ) {
9237c478bd9Sstevel@tonic-gate 		/* Clear contents in order to eliminate !PXE and PXENV
9247c478bd9Sstevel@tonic-gate 		 * signatures to prevent spurious detection via base
9257c478bd9Sstevel@tonic-gate 		 * memory scan.
9267c478bd9Sstevel@tonic-gate 		 */
9277c478bd9Sstevel@tonic-gate 		memset ( undi.driver_code, 0, undi.driver_code_size );
9287c478bd9Sstevel@tonic-gate 		/* forget_base_memory ( undi.driver_code, undi.driver_code_size ); */
9297c478bd9Sstevel@tonic-gate 		undi.driver_code = NULL;
9307c478bd9Sstevel@tonic-gate 		undi.driver_code_size = 0;
9317c478bd9Sstevel@tonic-gate 	}
9327c478bd9Sstevel@tonic-gate 	if ( undi.driver_data != NULL ) {
9337c478bd9Sstevel@tonic-gate 		/* forget_base_memory ( undi.driver_data, undi.driver_data_size ); */
9347c478bd9Sstevel@tonic-gate 		undi.driver_data = NULL;
9357c478bd9Sstevel@tonic-gate 		undi.driver_data_size = 0;
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 	/* !PXE structure now gone; memory freed */
9387c478bd9Sstevel@tonic-gate 	undi.pxe = NULL;
9397c478bd9Sstevel@tonic-gate 	return 1;
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate /**************************************************************************
9437c478bd9Sstevel@tonic-gate POLL - Wait for a frame
9447c478bd9Sstevel@tonic-gate ***************************************************************************/
undi_poll(struct nic * nic,int retrieve)9457c478bd9Sstevel@tonic-gate static int undi_poll(struct nic *nic, int retrieve)
9467c478bd9Sstevel@tonic-gate {
9477c478bd9Sstevel@tonic-gate 	/* Fun, fun, fun.  UNDI drivers don't use polling; they use
9487c478bd9Sstevel@tonic-gate 	 * interrupts.  We therefore cheat and pretend that an
9497c478bd9Sstevel@tonic-gate 	 * interrupt has occurred every time undi_poll() is called.
9507c478bd9Sstevel@tonic-gate 	 * This isn't too much of a hack; PCI devices share IRQs and
9517c478bd9Sstevel@tonic-gate 	 * so the first thing that a proper ISR should do is call
9527c478bd9Sstevel@tonic-gate 	 * PXENV_UNDI_ISR to determine whether or not the UNDI NIC
9537c478bd9Sstevel@tonic-gate 	 * generated the interrupt; there is no harm done by spurious
9547c478bd9Sstevel@tonic-gate 	 * calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
9557c478bd9Sstevel@tonic-gate 	 * handling them any more rapidly than the usual rate of
9567c478bd9Sstevel@tonic-gate 	 * undi_poll() being called even if we did implement a full
9577c478bd9Sstevel@tonic-gate 	 * ISR.  So it should work.  Ha!
9587c478bd9Sstevel@tonic-gate 	 *
9597c478bd9Sstevel@tonic-gate 	 * Addendum (21/10/03).  Some cards don't play nicely with
9607c478bd9Sstevel@tonic-gate 	 * this trick, so instead of doing it the easy way we have to
9617c478bd9Sstevel@tonic-gate 	 * go to all the hassle of installing a genuine interrupt
9627c478bd9Sstevel@tonic-gate 	 * service routine and dealing with the wonderful 8259
9637c478bd9Sstevel@tonic-gate 	 * Programmable Interrupt Controller.  Joy.
9647c478bd9Sstevel@tonic-gate 	 *
9657c478bd9Sstevel@tonic-gate 	 * (02/01/2005). A real UNDI ISR is now implemented in,
9667c478bd9Sstevel@tonic-gate 	 * following Figure 3-4 in PXE spec 2.0.  The interrupt
9677c478bd9Sstevel@tonic-gate 	 * handler, undi_irq_handler, issues PXENV_UNDI_ISR_IN_START.
9687c478bd9Sstevel@tonic-gate 	 * If the interrupt is ours, the handler sends EOI and bumps the
9697c478bd9Sstevel@tonic-gate 	 * undi_irq_trigger_count. This polled routine is equivalent
9707c478bd9Sstevel@tonic-gate 	 * to the "driver strategy routine".
9717c478bd9Sstevel@tonic-gate 	 *
9727c478bd9Sstevel@tonic-gate 	 * Another issue is that upper layer await_*() does not handle
9737c478bd9Sstevel@tonic-gate 	 * coalesced packets. The UNDI implementation on broadcom chips
9747c478bd9Sstevel@tonic-gate 	 * appear to combine interrupts. If we loop through GET_NEXT,
9757c478bd9Sstevel@tonic-gate 	 * we may hand up coalesced packets, resulting in drops, and
9767c478bd9Sstevel@tonic-gate 	 * severe time delay. As a temperary hack, we return as soon as
9777c478bd9Sstevel@tonic-gate 	 * we get something, remembering where we were (IN_PROCESS
9787c478bd9Sstevel@tonic-gate 	 * or GET_NEXT). This assume packets are never broken up.
9797c478bd9Sstevel@tonic-gate 	 * XXX Need to fix upper layer to handle coalesced data.
9807c478bd9Sstevel@tonic-gate 	 */
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	static int undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	/* See if a hardware interrupt has occurred since the last poll().
9857c478bd9Sstevel@tonic-gate 	 */
9867c478bd9Sstevel@tonic-gate 	switch ( undi_opcode ) {
9877c478bd9Sstevel@tonic-gate 	case PXENV_UNDI_ISR_IN_PROCESS:
9887c478bd9Sstevel@tonic-gate 		if ( ! undi_irq_triggered ( undi.irq ) )
9897c478bd9Sstevel@tonic-gate 			return 0;
9907c478bd9Sstevel@tonic-gate 	default:
9917c478bd9Sstevel@tonic-gate 		break;
9927c478bd9Sstevel@tonic-gate 	}
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	/* We have an interrupt or there is something left from
9957c478bd9Sstevel@tonic-gate 	 * last poll. Either way, we need to call UNDI ISR.
9967c478bd9Sstevel@tonic-gate 	 */
9977c478bd9Sstevel@tonic-gate 	nic->packetlen = 0;
9987c478bd9Sstevel@tonic-gate 	undi.pxs->undi_isr.FuncFlag = undi_opcode;
9997c478bd9Sstevel@tonic-gate 	/* there is no good way to handle this error */
10007c478bd9Sstevel@tonic-gate 	if ( ! eb_pxenv_undi_isr() ) {
10017c478bd9Sstevel@tonic-gate 		printf ("undi isr call failed: opcode = %d\n", undi_opcode);
10027c478bd9Sstevel@tonic-gate 		return 0;
10037c478bd9Sstevel@tonic-gate 	}
10047c478bd9Sstevel@tonic-gate 	switch ( undi.pxs->undi_isr.FuncFlag ) {
10057c478bd9Sstevel@tonic-gate 	case PXENV_UNDI_ISR_OUT_DONE:
10067c478bd9Sstevel@tonic-gate 		/* Set opcode back to IN_PROCESS and wait for next intr */
10077c478bd9Sstevel@tonic-gate 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
10087c478bd9Sstevel@tonic-gate 		return 0;
10097c478bd9Sstevel@tonic-gate 	case PXENV_UNDI_ISR_OUT_TRANSMIT:
10107c478bd9Sstevel@tonic-gate 		/* We really don't care about transmission complete
10117c478bd9Sstevel@tonic-gate 		 * interrupts. Move on to next frame.
10127c478bd9Sstevel@tonic-gate 		 */
10137c478bd9Sstevel@tonic-gate 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10147c478bd9Sstevel@tonic-gate 		return 0;
10157c478bd9Sstevel@tonic-gate 	case PXENV_UNDI_ISR_OUT_BUSY:
10167c478bd9Sstevel@tonic-gate 		/* This should never happen.
10177c478bd9Sstevel@tonic-gate 		 */
10187c478bd9Sstevel@tonic-gate 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10197c478bd9Sstevel@tonic-gate 		printf ( "UNDI ISR thinks it's being re-entered!\n"
10207c478bd9Sstevel@tonic-gate 			 "Aborting receive\n" );
10217c478bd9Sstevel@tonic-gate 		return 0;
10227c478bd9Sstevel@tonic-gate 	case PXENV_UNDI_ISR_OUT_RECEIVE:
10237c478bd9Sstevel@tonic-gate 		/* Copy data to receive buffer and move on to next frame */
10247c478bd9Sstevel@tonic-gate 		undi_opcode = PXENV_UNDI_ISR_IN_GET_NEXT;
10257c478bd9Sstevel@tonic-gate 		memcpy ( nic->packet + nic->packetlen,
10267c478bd9Sstevel@tonic-gate 			 VIRTUAL( undi.pxs->undi_isr.Frame.segment,
10277c478bd9Sstevel@tonic-gate 				  undi.pxs->undi_isr.Frame.offset ),
10287c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_isr.BufferLength );
10297c478bd9Sstevel@tonic-gate 		nic->packetlen += undi.pxs->undi_isr.BufferLength;
10307c478bd9Sstevel@tonic-gate 		break;
10317c478bd9Sstevel@tonic-gate 	default:
10327c478bd9Sstevel@tonic-gate 		undi_opcode = PXENV_UNDI_ISR_IN_PROCESS;
10337c478bd9Sstevel@tonic-gate 		printf ( "UNDI ISR returned bizzare status code %d\n",
10347c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_isr.FuncFlag );
10357c478bd9Sstevel@tonic-gate 	}
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	return nic->packetlen > 0 ? 1 : 0;
10387c478bd9Sstevel@tonic-gate }
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate /**************************************************************************
10417c478bd9Sstevel@tonic-gate TRANSMIT - Transmit a frame
10427c478bd9Sstevel@tonic-gate ***************************************************************************/
undi_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)10437c478bd9Sstevel@tonic-gate static void undi_transmit(
10447c478bd9Sstevel@tonic-gate 	struct nic *nic,
10457c478bd9Sstevel@tonic-gate 	const char *d,			/* Destination */
10467c478bd9Sstevel@tonic-gate 	unsigned int t,			/* Type */
10477c478bd9Sstevel@tonic-gate 	unsigned int s,			/* size */
10487c478bd9Sstevel@tonic-gate 	const char *p)			/* Packet */
10497c478bd9Sstevel@tonic-gate {
10507c478bd9Sstevel@tonic-gate 	/* Inhibit compiler warning about unused parameter nic */
10517c478bd9Sstevel@tonic-gate 	if ( nic == NULL ) {};
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	/* Copy destination to buffer in base memory */
10547c478bd9Sstevel@tonic-gate 	memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	/* Translate packet type to UNDI packet type */
10577c478bd9Sstevel@tonic-gate 	switch ( t ) {
10587c478bd9Sstevel@tonic-gate 	case IP :  undi.pxs->undi_transmit.Protocol = P_IP;   break;
10597c478bd9Sstevel@tonic-gate 	case ARP:  undi.pxs->undi_transmit.Protocol = P_ARP;  break;
10607c478bd9Sstevel@tonic-gate 	case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break;
10617c478bd9Sstevel@tonic-gate 	default: undi.pxs->undi_transmit.Protocol = P_UNKNOWN; break;
10627c478bd9Sstevel@tonic-gate 	}
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	/* Store packet length in TBD */
10657c478bd9Sstevel@tonic-gate 	undi.xmit_data->tbd.ImmedLength = s;
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 	/* Check to see if data to be transmitted is currently in base
10687c478bd9Sstevel@tonic-gate 	 * memory.  If not, allocate temporary storage in base memory
10697c478bd9Sstevel@tonic-gate 	 * and copy it there.
10707c478bd9Sstevel@tonic-gate 	 */
10717c478bd9Sstevel@tonic-gate 	if ( SEGMENT( p ) <= 0xffff ) {
10727c478bd9Sstevel@tonic-gate 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
10737c478bd9Sstevel@tonic-gate 		undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
10747c478bd9Sstevel@tonic-gate 	} else {
10757c478bd9Sstevel@tonic-gate 		memcpy ( undi.xmit_buffer, p, s );
10767c478bd9Sstevel@tonic-gate 		undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer );
10777c478bd9Sstevel@tonic-gate 		undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer );
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	eb_pxenv_undi_transmit_packet();
10817c478bd9Sstevel@tonic-gate }
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate /**************************************************************************
10847c478bd9Sstevel@tonic-gate DISABLE - Turn off ethernet interface
10857c478bd9Sstevel@tonic-gate ***************************************************************************/
undi_disable(struct dev * dev)10867c478bd9Sstevel@tonic-gate static void undi_disable(struct dev *dev)
10877c478bd9Sstevel@tonic-gate {
10887c478bd9Sstevel@tonic-gate 	/* Inhibit compiler warning about unused parameter dev */
10897c478bd9Sstevel@tonic-gate 	if ( dev == NULL ) {};
10907c478bd9Sstevel@tonic-gate 	undi_full_shutdown();
10917c478bd9Sstevel@tonic-gate 	free_base_mem_data();
10927c478bd9Sstevel@tonic-gate }
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate /**************************************************************************
10957c478bd9Sstevel@tonic-gate PROBE - Look for an adapter, this routine's visible to the outside
10967c478bd9Sstevel@tonic-gate ***************************************************************************/
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate /* Locate an UNDI driver by first scanning through base memory for an
10997c478bd9Sstevel@tonic-gate  * installed driver and then by scanning for UNDI ROMs and attempting
11007c478bd9Sstevel@tonic-gate  * to install their drivers.
11017c478bd9Sstevel@tonic-gate  */
11027c478bd9Sstevel@tonic-gate 
hunt_pixies_and_undi_roms(void)11037c478bd9Sstevel@tonic-gate int hunt_pixies_and_undi_roms ( void ) {
11047c478bd9Sstevel@tonic-gate 	static uint8_t hunt_type = HUNT_FOR_PIXIES;
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	if ( hunt_type == HUNT_FOR_PIXIES ) {
11077c478bd9Sstevel@tonic-gate 		if ( hunt_pixie() ) {
11087c478bd9Sstevel@tonic-gate 			return 1;
11097c478bd9Sstevel@tonic-gate 		}
11107c478bd9Sstevel@tonic-gate 	}
11117c478bd9Sstevel@tonic-gate 	hunt_type = HUNT_FOR_UNDI_ROMS;
11127c478bd9Sstevel@tonic-gate 	while ( hunt_undi_rom() ) {
11137c478bd9Sstevel@tonic-gate 		if ( undi_loader() ) {
11147c478bd9Sstevel@tonic-gate 			return 1;
11157c478bd9Sstevel@tonic-gate 		}
11167c478bd9Sstevel@tonic-gate 		undi_full_shutdown(); /* Free any allocated memory */
11177c478bd9Sstevel@tonic-gate 	}
11187c478bd9Sstevel@tonic-gate 	hunt_type = HUNT_FOR_PIXIES;
11197c478bd9Sstevel@tonic-gate 	return 0;
11207c478bd9Sstevel@tonic-gate }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate /* The actual Etherboot probe routine.
11237c478bd9Sstevel@tonic-gate  */
11247c478bd9Sstevel@tonic-gate 
undi_probe(struct dev * dev,struct pci_device * pci)11257c478bd9Sstevel@tonic-gate static int undi_probe(struct dev *dev, struct pci_device *pci)
11267c478bd9Sstevel@tonic-gate {
11277c478bd9Sstevel@tonic-gate 	struct nic *nic = (struct nic *)dev;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/* Zero out global undi structure */
11307c478bd9Sstevel@tonic-gate 	memset ( &undi, 0, sizeof(undi) );
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	/* Store PCI parameters; we will need them to initialize the UNDI
11337c478bd9Sstevel@tonic-gate 	 * driver later.
11347c478bd9Sstevel@tonic-gate 	 */
11357c478bd9Sstevel@tonic-gate 	memcpy ( &undi.pci, pci, sizeof(undi.pci) );
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	/* Find the BIOS' $PnP structure */
11387c478bd9Sstevel@tonic-gate 	if ( ! hunt_pnp_bios() ) {
11397c478bd9Sstevel@tonic-gate 		printf ( "No PnP BIOS found; aborting\n" );
11407c478bd9Sstevel@tonic-gate 		return 0;
11417c478bd9Sstevel@tonic-gate 	}
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/* Allocate base memory data structures */
11447c478bd9Sstevel@tonic-gate 	if ( ! allocate_base_mem_data() ) return 0;
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	/* Search thoroughly for UNDI drivers */
11477c478bd9Sstevel@tonic-gate 	for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
11487c478bd9Sstevel@tonic-gate 		/* Try to initialise UNDI driver */
11497c478bd9Sstevel@tonic-gate 		DBG ( "Initializing UNDI driver.  Please wait...\n" );
11507c478bd9Sstevel@tonic-gate 		if ( ! undi_full_startup() ) {
11517c478bd9Sstevel@tonic-gate 			if ( undi.pxs->Status ==
11527c478bd9Sstevel@tonic-gate 			     PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
11537c478bd9Sstevel@tonic-gate 				DBG ( "Cable not connected (code %#hx)\n",
11547c478bd9Sstevel@tonic-gate 					 PXENV_STATUS_UNDI_MEDIATEST_FAILED );
11557c478bd9Sstevel@tonic-gate 			}
11567c478bd9Sstevel@tonic-gate 			continue;
11577c478bd9Sstevel@tonic-gate 		}
11587c478bd9Sstevel@tonic-gate 		/* Basic information: MAC, IO addr, IRQ */
11597c478bd9Sstevel@tonic-gate 		if ( ! eb_pxenv_undi_get_information() ) continue;
11607c478bd9Sstevel@tonic-gate 		DBG ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
11617c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_information.BaseIo,
11627c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_information.IntNumber,
11637c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_information.CurrentNodeAddress );
11647c478bd9Sstevel@tonic-gate 		/* Fill out MAC address in nic structure */
11657c478bd9Sstevel@tonic-gate 		memcpy ( nic->node_addr,
11667c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_information.CurrentNodeAddress,
11677c478bd9Sstevel@tonic-gate 			 ETH_ALEN );
11687c478bd9Sstevel@tonic-gate 		/* More diagnostic information including link speed */
11697c478bd9Sstevel@tonic-gate 		if ( ! eb_pxenv_undi_get_iface_info() ) continue;
11707c478bd9Sstevel@tonic-gate 		printf ( "  NDIS type %s interface at %d Mbps\n",
11717c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_iface_info.IfaceType,
11727c478bd9Sstevel@tonic-gate 			 undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
11737c478bd9Sstevel@tonic-gate 		DBG ("UNDI Stack at %#hx:%#hx",UNDI_STACK_SEG, UNDI_STACK_OFF);
11747c478bd9Sstevel@tonic-gate 		dev->disable  = undi_disable;
11757c478bd9Sstevel@tonic-gate 		nic->poll     = undi_poll;
11767c478bd9Sstevel@tonic-gate 		nic->transmit = undi_transmit;
11777c478bd9Sstevel@tonic-gate 		return 1;
11787c478bd9Sstevel@tonic-gate 	}
11797c478bd9Sstevel@tonic-gate 	undi_disable ( dev ); /* To free base memory structures */
11807c478bd9Sstevel@tonic-gate 	return 0;
11817c478bd9Sstevel@tonic-gate }
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate /* UNDI driver states that it is suitable for any PCI NIC (i.e. any
11847c478bd9Sstevel@tonic-gate  * PCI device of class PCI_CLASS_NETWORK_ETHERNET).  If there are any
11857c478bd9Sstevel@tonic-gate  * obscure UNDI NICs that have the incorrect PCI class, add them to
11867c478bd9Sstevel@tonic-gate  * this list.
11877c478bd9Sstevel@tonic-gate  */
11887c478bd9Sstevel@tonic-gate 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")
11917c478bd9Sstevel@tonic-gate };
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate struct pci_driver undi_driver = {
11947c478bd9Sstevel@tonic-gate 	.type     = NIC_DRIVER,
11957c478bd9Sstevel@tonic-gate 	.name     = "UNDI",
11967c478bd9Sstevel@tonic-gate 	.probe    = undi_probe,
11977c478bd9Sstevel@tonic-gate 	.ids      = undi_nics,
11987c478bd9Sstevel@tonic-gate  	.id_count = sizeof(undi_nics)/sizeof(undi_nics[0]),
11997c478bd9Sstevel@tonic-gate 	.class    = PCI_CLASS_NETWORK_ETHERNET,
12007c478bd9Sstevel@tonic-gate };
120199ed6083Sszhou 
120299ed6083Sszhou /************************************************
120399ed6083Sszhou  * Code for reusing the BIOS provided pxe stack
120499ed6083Sszhou  */
120599ed6083Sszhou 
120699ed6083Sszhou /* Verify !PXE structure saved by pxeloader. */
undi_bios_pxe(void ** dhcpreply)120799ed6083Sszhou int undi_bios_pxe(void **dhcpreply)
120899ed6083Sszhou {
120999ed6083Sszhou 	pxe_t *pxe;
121099ed6083Sszhou 	uint16_t *ptr = (uint16_t *)0x7C80;
121199ed6083Sszhou 
121299ed6083Sszhou 	pxe = (pxe_t *) VIRTUAL(ptr[0], ptr[1]);
121399ed6083Sszhou 	if (memcmp(pxe->Signature, "!PXE", 4) != 0) {
121499ed6083Sszhou 		DBG ("invalid !PXE signature at %x:%x\n", ptr[0], ptr[1]);
121599ed6083Sszhou 		return 0;
121699ed6083Sszhou 	}
121799ed6083Sszhou 
121899ed6083Sszhou 	if (checksum(pxe, sizeof(pxe_t)) != 0) {
121999ed6083Sszhou 		DBG ("invalid checksum\n");
122099ed6083Sszhou 		return 0;
122199ed6083Sszhou 	}
122299ed6083Sszhou 
122399ed6083Sszhou 	/* Zero out global undi structure */
122499ed6083Sszhou 	memset (&undi, 0, sizeof(undi));
122599ed6083Sszhou 
122699ed6083Sszhou 	/* Allocate base memory data structures */
122799ed6083Sszhou 	if (! allocate_base_mem_data()) return 0;
122899ed6083Sszhou 
122999ed6083Sszhou 	undi.pxe = pxe;
123099ed6083Sszhou 	pxe_dump();
123199ed6083Sszhou 
123299ed6083Sszhou 	if (!eb_pxenv_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, dhcpreply)) {
123399ed6083Sszhou 		DBG ("failed to get cached DHCP reply\n");
123499ed6083Sszhou 		return 0;
123599ed6083Sszhou 	}
123699ed6083Sszhou 	return 1;
123799ed6083Sszhou }
123899ed6083Sszhou 
undi_pxe_disable(void)123999ed6083Sszhou void undi_pxe_disable(void)
124099ed6083Sszhou {
124199ed6083Sszhou 	/* full shutdown is problematic for some machines */
124299ed6083Sszhou 	(void) eb_pxenv_undi_shutdown();
124399ed6083Sszhou }
124499ed6083Sszhou 
124599ed6083Sszhou /*
124699ed6083Sszhou  * Various helper functions for dhcp and tftp
124799ed6083Sszhou  */
eb_pxenv_get_cached_info(uint8_t type,void ** info)124899ed6083Sszhou int eb_pxenv_get_cached_info (uint8_t type, void **info)
124999ed6083Sszhou {
125099ed6083Sszhou 	int success;
125199ed6083Sszhou 
125299ed6083Sszhou 	memset(undi.pxs, 0, sizeof (undi.pxs));
125399ed6083Sszhou 	/* Segment:offset pointer to DestAddr in base memory */
125499ed6083Sszhou 	undi.pxs->get_cached_info.PacketType = type;
125599ed6083Sszhou 	undi.pxs->get_cached_info.BufferSize = 0;
125699ed6083Sszhou 	undi.pxs->get_cached_info.Buffer.segment = 0;
125799ed6083Sszhou 	undi.pxs->get_cached_info.Buffer.offset = 0;
125899ed6083Sszhou 
125999ed6083Sszhou 	success = undi_call(PXENV_GET_CACHED_INFO);
126099ed6083Sszhou 	DBG ("PXENV_GET_CACHED_INFO <= Status=%s\n", UNDI_STATUS(undi.pxs));
126199ed6083Sszhou 
126299ed6083Sszhou 	*info = (void *)VIRTUAL(undi.pxs->get_cached_info.Buffer.segment,
126399ed6083Sszhou 	    undi.pxs->get_cached_info.Buffer.offset);
126499ed6083Sszhou 	return success;
126599ed6083Sszhou }
126699ed6083Sszhou 
126799ed6083Sszhou /* tftp help routines */
eb_pxenv_tftp_open(char * file,IP4_t serverip,IP4_t gatewayip,uint16_t * pktlen)126899ed6083Sszhou int eb_pxenv_tftp_open(char *file, IP4_t serverip, IP4_t gatewayip,
126999ed6083Sszhou     uint16_t *pktlen)
127099ed6083Sszhou {
127199ed6083Sszhou 	int success;
127299ed6083Sszhou 	memset(undi.pxs, 0, sizeof (undi.pxs));
127399ed6083Sszhou 	undi.pxs->tftp_open.ServerIPAddress = serverip;
127499ed6083Sszhou 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
127599ed6083Sszhou 	undi.pxs->tftp_open.TFTPPort = htons(TFTP_PORT);
127699ed6083Sszhou 	undi.pxs->tftp_open.PacketSize = TFTP_MAX_PACKET;
127799ed6083Sszhou 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
127899ed6083Sszhou 	success = undi_call(PXENV_TFTP_OPEN);
127999ed6083Sszhou 	DBG ("PXENV_TFTP_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs));
128099ed6083Sszhou 	*pktlen = undi.pxs->tftp_open.PacketSize;
128199ed6083Sszhou 	return success;
128299ed6083Sszhou }
128399ed6083Sszhou 
eb_pxenv_tftp_read(uint8_t * buf,uint16_t * len)128499ed6083Sszhou int eb_pxenv_tftp_read(uint8_t *buf, uint16_t *len)
128599ed6083Sszhou {
128699ed6083Sszhou 	static int tftp_count = 0;
128799ed6083Sszhou 
128899ed6083Sszhou 	int success;
128999ed6083Sszhou 	memset(undi.pxs, 0, sizeof (undi.pxs));
129099ed6083Sszhou 	undi.pxs->tftp_read.Buffer.segment = SEGMENT(buf);
129199ed6083Sszhou 	undi.pxs->tftp_read.Buffer.offset = OFFSET(buf);
129299ed6083Sszhou 	success = undi_call(PXENV_TFTP_READ);
129399ed6083Sszhou 	DBG ("PXENV_TFTP_READ <= Status=%s\n", UNDI_STATUS(undi.pxs));
129499ed6083Sszhou 	*len = undi.pxs->tftp_read.BufferSize;
129599ed6083Sszhou 	tftp_count++;
129699ed6083Sszhou 	if ((tftp_count % 1000) == 0)
12971fac5a60Ssetje 		noisy_printf(".");
129899ed6083Sszhou 	return success;
129999ed6083Sszhou }
130099ed6083Sszhou 
eb_pxenv_tftp_close(void)130199ed6083Sszhou int eb_pxenv_tftp_close(void)
130299ed6083Sszhou {
130399ed6083Sszhou 	int success;
130499ed6083Sszhou 	memset(undi.pxs, 0, sizeof (undi.pxs));
130599ed6083Sszhou 	success = undi_call(PXENV_TFTP_CLOSE);
130699ed6083Sszhou 	DBG ("PXENV_TFTP_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs));
130799ed6083Sszhou 	return success;
130899ed6083Sszhou }
130999ed6083Sszhou 
eb_pxenv_tftp_get_fsize(char * file,IP4_t serverip,IP4_t gatewayip,uint32_t * fsize)131099ed6083Sszhou int eb_pxenv_tftp_get_fsize(char *file, IP4_t serverip, IP4_t gatewayip,
131199ed6083Sszhou     uint32_t *fsize)
131299ed6083Sszhou {
131399ed6083Sszhou 	int success;
131499ed6083Sszhou 	memset(undi.pxs, 0, sizeof (undi.pxs));
131599ed6083Sszhou 	undi.pxs->tftp_open.ServerIPAddress = serverip;
131699ed6083Sszhou 	undi.pxs->tftp_open.GatewayIPAddress = gatewayip;
131799ed6083Sszhou 	(void) sprintf(undi.pxs->tftp_open.FileName, "%s", file);
131899ed6083Sszhou 	success = undi_call(PXENV_TFTP_GET_FSIZE);
131999ed6083Sszhou 	DBG ("PXENV_TFTP_GET_FSIZE <= Status=%s\n", UNDI_STATUS(undi.pxs));
132099ed6083Sszhou 	*fsize = undi.pxs->tftp_get_fsize.FileSize;
132199ed6083Sszhou 	return success;
132299ed6083Sszhou }
1323