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