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