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