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
10 ***************************************************************************/
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
20 #warning "undi.c needs to destroy the !PXE signature when freeing a pixie"
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.
31 /* to get some global routines like printf */
32 #include "etherboot.h"
33 /* to get the interface to the body of the program */
35 /* to get the PCI support functions, if this is a PCI NIC */
37 /* UNDI and PXE defines. Includes pxe.h. */
39 /* 8259 PIC defines */
43 /* E820 map mangler */
46 /* NIC specific static variables go here */
47 static undi_t undi = {
54 .base_mem_data = NULL,
56 .driver_code_size = 0,
58 .driver_data_size = 0,
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 );
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)
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 */
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 )
101 /**************************************************************************
103 **************************************************************************/
108 static uint8_t checksum ( void *block, size_t size ) {
111 for ( i = 0; i < size; i++ ) {
112 sum += ( ( uint8_t * ) block )[i];
117 /* Print the status of a !PXE structure
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 );
131 /* Allocate/free space for structures that must reside in base memory
134 static int allocate_base_mem_data ( void ) {
135 /* Allocate space in base memory.
136 * Initialise pointers to base memory structures.
138 if ( undi.base_mem_data == NULL ) {
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();
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;
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;
162 undi.xmit_data = NULL;
163 undi.xmit_buffer = NULL;
164 copy_undi_irq_handler ( NULL, 0 );
169 static void assemble_firing_squad ( firing_squad_lineup_t *lineup,
170 void *start, size_t size,
171 firing_squad_shoot_t shoot ) {
175 int start_kb = virt_to_phys(start) >> 10;
176 int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
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 ) );
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;
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 ),
203 shoot_last_target = shoot_this_target;
212 #define DBG(...) printf ( __VA_ARGS__ )
217 #define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
219 ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
220 "FAILURE" : "UNKNOWN" ) )
222 /**************************************************************************
223 * Base memory scanning functions
224 **************************************************************************/
226 /* Locate the $PnP structure indicating a PnP BIOS.
229 static int hunt_pnp_bios ( void ) {
230 uint32_t off = 0x10000;
232 printf ( "Hunting for PnP BIOS..." );
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..." );
246 printf ( "none found\n" );
247 undi.pnp_bios = NULL;
251 /* Locate the !PXE structure indicating a loaded UNDI driver.
254 static int hunt_pixie ( void ) {
255 static uint32_t ptr = 0;
258 printf ( "Hunting for pixies..." );
259 if ( ptr == 0 ) ptr = 0xa0000;
260 while ( ptr > 0x10000 ) {
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..." );
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 "
277 printf ( "\nIgnoring and continuing, but this "
278 "may cause problems later!\n\n" );
284 printf ( "Resetting pixie...\n" );
285 undi_unload_base_code();
286 eb_pxenv_stop_undi();
291 printf ( "none found\n" );
296 /* Locate PCI PnP ROMs.
299 static int hunt_rom ( void ) {
300 static uint32_t ptr = 0;
302 /* If we are not a PCI device, we cannot search for a ROM that
305 if ( ! undi.pci->vendor_id )
308 printf ( "Hunting for ROMs..." );
309 if ( ptr == 0 ) ptr = 0x100000;
310 while ( ptr > 0x0c0000 ) {
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;
317 printf ( "found 55AA at %x...", ptr );
318 if ( undi.rom->pcir_off == 0 ) {
319 printf ( "not a PCI ROM\n..." );
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..." );
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...",
334 undi.pci->device_id );
337 if ( undi.rom->pnp_off == 0 ) {
338 printf ( "not a PnP ROM\n..." );
341 pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
343 if ( pnp_header->signature != PNP_SIGNATURE ) {
344 printf ( "invalid $PnP signature\n..." );
347 if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
348 printf ( "invalid PnP checksum\n..." );
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 );
359 printf ( "none found\n" );
365 /* Locate ROMs containing UNDI drivers.
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" );
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" );
380 if ( checksum ( undi.undi_rom_id,
381 undi.undi_rom_id->struct_length ) != 0 ) {
382 printf ( "Invalid checksum\n" );
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] );
394 /**************************************************************************
395 * Low-level UNDI API call wrappers
396 **************************************************************************/
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
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;
412 } PACKED in_stack = {
413 { routine_off, routine_seg }, st0, st1, st2
416 RM_FRAGMENT(rm_undi_call,
417 "popw %di\n\t" /* %es:di = routine */
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"
423 "pushw %es\n\t" /* routine address to stack */
425 "lret\n\t" /* calculated lcall */
426 "\n2:\n\t" /* continuation point */
429 /* Parameters are left on stack: set out_stack = in_stack */
430 ret = real_call ( rm_undi_call, &in_stack, &in_stack );
432 /* UNDI API calls may rudely change the status of A20 and not
433 * bother to restore it afterwards. Intel is known to be
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.
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.
450 static int undi_call_loader ( void ) {
451 PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
453 /* Hide Etherboot around the loader, so that the PXE stack
454 * doesn't trash our memory areas
456 install_e820mangler ( undi.base_mem_data->e820mangler );
458 pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
459 undi.undi_rom_id->undi_loader_off,
462 0 /* Unused for UNDI loader API */ );
463 if ( !unhide_etherboot() ) {
464 printf ( "FATAL: corrupt INT15\n" );
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",
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.
478 * Two versions: undi_call() will automatically report any failure
479 * codes, undi_call_silent() will not.
482 static int undi_call_silent ( uint16_t opcode ) {
483 PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
485 pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
486 undi.pxe->EntryPointSP.offset,
489 SEGMENT( undi.pxs ) );
490 /* Return 1 for success, to be consistent with other routines */
491 return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
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 );
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.
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
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.
516 uint16_t nontrivial_irq_previous_trigger_count = 0;
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)
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"
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"
537 /* Assume that PXE stack will corrupt everything */
546 /* Set up parameters for call */
547 "movw $" RM_STR(PXENV_UNDI_ISR_IN_START) ", %ds:(undi_isr_FuncFlag-irqstart)\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 */
555 /* Make PXE API call */
556 "lcall *%ds:(entry-irqstart)\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"
564 "cmpw $" RM_STR(PXENV_UNDI_ISR_OUT_OURS) ", %cs:(undi_isr_FuncFlag-irqstart)\n\t"
566 /* Increment count_ours if so */
567 "incw %ds:(count_ours-irqstart)\n\t"
569 /* Increment count_all anyway */
570 "incw %ds:(count_all-irqstart)\n\t"
571 /* Restore registers and return */
578 /* Chain to acknowledge the interrupt */
579 "cmpb $0, %cs:(irq_chain-irqstart)\n\t"
581 "ljmp %cs:(chain_to-irqstart)\n\t"
589 memcpy ( target, nontrivial_irq_handler, NONTRIVIAL_IRQ_HANDLER_SIZE );
594 static int install_nontrivial_irq_handler ( irq_t irq ) {
595 undi_irq_handler_t *handler =
596 &undi.base_mem_data->nontrivial_irq_handler;
599 printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" );
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.
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);
621 static int remove_nontrivial_irq_handler ( irq_t irq ) {
622 undi_irq_handler_t *handler =
623 &undi.base_mem_data->nontrivial_irq_handler;
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);
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 );
640 nontrivial_irq_previous_trigger_count =
641 nontrivial_irq_this_trigger_count;
642 return triggered ? 1 : 0;
645 static void nontrivial_irq_debug ( irq_t irq ) {
646 undi_irq_handler_t *handler =
647 &undi.base_mem_data->nontrivial_irq_handler;
649 printf ( "IRQ %d triggered %d times (%d of which were ours)\n",
650 irq, handler->count_all, handler->count_ours );
652 #endif /* UNDI_NONTRIVIAL_IRQ */
654 /**************************************************************************
655 * High-level UNDI API call wrappers
656 **************************************************************************/
658 /* Install the UNDI driver from a located UNDI ROM.
661 static int undi_loader ( void ) {
664 if ( ! undi.pci->vendor_id ) {
665 printf ( "ERROR: attempted to call loader of an ISA ROM?\n" );
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)
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)
679 if ( undi.pnp_bios ) {
680 undi.pxs->loader.es = 0xf000;
681 undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
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;
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 );
696 undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
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 );
705 undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
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 );
710 /* Do the API call to install the loader */
711 if ( ! undi_call_loader () ) return 0;
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" );
721 if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
722 printf ( "invalid checksum\n" );
731 /* Start the UNDI driver.
734 static int eb_pxenv_start_undi ( void ) {
737 /* AX contains PCI bus:devfn (PCI specification) */
738 undi.pxs->start_undi.ax = undi.pci->busdevfn;
740 /* BX and DX set to 0xffff for non-ISAPnP devices
741 * (BIOS boot specification)
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)
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;
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;
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;
768 static int eb_pxenv_undi_startup ( void ) {
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;
778 static int eb_pxenv_undi_cleanup ( void ) {
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) );
787 static int eb_pxenv_undi_initialize ( void ) {
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;
800 static int eb_pxenv_undi_shutdown ( void ) {
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) );
807 undi.initialized = 0;
813 static int eb_pxenv_undi_open ( void ) {
816 undi.pxs->undi_open.OpenFlag = 0;
817 undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
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;
831 static int eb_pxenv_undi_close ( void ) {
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;
841 static int eb_pxenv_undi_transmit_packet ( void ) {
843 static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
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;
850 undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
853 /* Zero reserved dwords */
854 undi.pxs->undi_transmit.Reserved[0] = 0;
855 undi.pxs->undi_transmit.Reserved[1] = 0;
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 );
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 );
867 /* Use only the "immediate" part of the TBD */
868 undi.xmit_data->tbd.DataBlkCount = 0;
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) );
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).
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) );
904 static int eb_pxenv_undi_get_information ( void ) {
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 );
928 static int eb_pxenv_undi_get_iface_info ( void ) {
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 );
942 static int eb_pxenv_undi_isr ( void ) {
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 );
962 static int eb_pxenv_stop_undi ( void ) {
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;
972 static int eb_pxenv_unload_stack ( void ) {
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" ) ) ) );
990 static int eb_pxenv_stop_base ( void ) {
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) );
999 /* Unload UNDI base code (if any present) and free memory.
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;
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
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.
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 ) )
1032 printf ( "Could not free memory allocated to PXE base code: "
1033 "possible memory leak\n" );
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.
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.
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;
1067 /* Free and reallocate our own base memory data structures, to
1068 * allow the freed base-code blocks to be fully released.
1070 free_base_mem_data();
1071 if ( ! allocate_base_mem_data() ) {
1072 printf ( "FATAL: memory unaccountably lost\n" );
1079 /* UNDI full initialization
1081 * This calls all the various UNDI initialization routines in sequence.
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;
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;
1104 /* UNDI full shutdown
1106 * This calls all the various UNDI shutdown routines in sequence and
1107 * also frees any memory that it can.
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.
1116 if ( undi.driver_code == NULL ) {
1117 undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
1119 undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
1121 if ( undi.driver_data == NULL ) {
1122 undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
1124 undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
1127 /* Ignore errors and continue in the hope of shutting
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).
1140 eb_pxenv_undi_shutdown();
1142 if ( undi.irq != IRQ_NONE ) {
1143 remove_undi_irq_handler ( undi.irq );
1144 undi.irq = IRQ_NONE;
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
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");
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
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;
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;
1177 /* !PXE structure now gone; memory freed */
1182 /**************************************************************************
1183 POLL - Wait for a frame
1184 ***************************************************************************/
1185 static int undi_poll(struct nic *nic, int retrieve)
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!
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.
1206 /* See if a hardware interrupt has occurred since the last poll().
1208 if ( ! undi_irq_triggered ( undi.irq ) ) return 0;
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.
1216 if ( ! retrieve ) return 1;
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.
1224 /* Ask the UNDI driver if this is "our" interrupt.
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
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
1240 send_specific_eoi ( undi.irq );
1245 /* At this stage, the device should have cleared its interrupt
1246 * line so we can send EOI to the 8259.
1248 send_specific_eoi ( undi.irq );
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.
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
1266 case PXENV_UNDI_ISR_OUT_BUSY:
1267 /* This should never happen.
1269 printf ( "UNDI ISR thinks it's being re-entered!\n"
1270 "Aborting receive\n" );
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;
1281 printf ( "UNDI ISR returned bizzare status code %d\n",
1282 undi.pxs->undi_isr.FuncFlag );
1284 undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
1285 if ( ! eb_pxenv_undi_isr() ) return 0;
1287 return nic->packetlen > 0 ? 1 : 0;
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 */
1300 /* Copy destination to buffer in base memory */
1301 memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
1303 /* Translate packet type to UNDI packet type */
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 );
1312 /* Store packet length in TBD */
1313 undi.xmit_data->tbd.ImmedLength = s;
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.
1319 if ( SEGMENT( p ) <= 0xffff ) {
1320 undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
1321 undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
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 );
1328 eb_pxenv_undi_transmit_packet();
1331 /**************************************************************************
1332 DISABLE - Turn off ethernet interface
1333 ***************************************************************************/
1334 static void undi_disable ( struct nic *nic __unused,
1335 struct pci_device *pci __unused ) {
1336 undi_full_shutdown();
1337 free_base_mem_data();
1340 /**************************************************************************
1341 PROBE - Look for an adapter, this routine's visible to the outside
1342 ***************************************************************************/
1344 /* Locate an UNDI driver by first scanning through base memory for an
1345 * installed driver and then by scanning for UNDI ROMs and attempting
1346 * to install their drivers.
1349 static int hunt_pixies_and_undi_roms ( void ) {
1350 static uint8_t hunt_type = HUNT_FOR_PIXIES;
1352 if ( hunt_type == HUNT_FOR_PIXIES ) {
1353 if ( hunt_pixie() ) {
1357 hunt_type = HUNT_FOR_UNDI_ROMS;
1358 while ( hunt_undi_rom() ) {
1359 if ( undi_loader() ) {
1362 undi_full_shutdown(); /* Free any allocated memory */
1364 hunt_type = HUNT_FOR_PIXIES;
1368 static struct nic_operations undi_operations = {
1369 .connect = dummy_connect,
1371 .transmit = undi_transmit,
1375 /* The actual Etherboot probe routine.
1378 static int undi_probe ( struct nic *nic, struct pci_device *pci ) {
1380 /* Zero out global undi structure */
1381 memset ( &undi, 0, sizeof(undi) );
1383 /* Store PCI parameters; we will need them to initialize the
1384 * UNDI driver later. If not a PCI device, leave as 0.
1388 /* Find the BIOS' $PnP structure */
1389 if ( ! hunt_pnp_bios() ) {
1390 /* Not all PXE stacks actually insist on a PnP BIOS.
1391 * In particular, an Etherboot PXE stack will work
1392 * just fine without one.
1394 * We used to make this a fatal error, but now we just
1395 * warn and continue. Note that this is necessary in
1396 * order to be able to debug the Etherboot PXE stack
1397 * under Bochs, since Bochs' BIOS is non-PnP.
1399 printf ( "WARNING: No PnP BIOS found\n" );
1402 /* Allocate base memory data structures */
1403 if ( ! allocate_base_mem_data() ) return 0;
1405 /* Search thoroughly for UNDI drivers */
1406 for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
1407 /* Try to initialise UNDI driver */
1408 printf ( "Initializing UNDI driver. Please wait...\n" );
1409 if ( ! undi_full_startup() ) {
1410 if ( undi.pxs->Status ==
1411 PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
1412 printf ( "Cable not connected (code %#hx)\n",
1413 PXENV_STATUS_UNDI_MEDIATEST_FAILED );
1417 /* Basic information: MAC, IO addr, IRQ */
1418 if ( ! eb_pxenv_undi_get_information() ) continue;
1419 printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
1420 undi.pxs->undi_get_information.BaseIo,
1421 undi.pxs->undi_get_information.IntNumber,
1422 undi.pxs->undi_get_information.CurrentNodeAddress );
1423 /* Fill out MAC address in nic structure */
1424 memcpy ( nic->node_addr,
1425 undi.pxs->undi_get_information.CurrentNodeAddress,
1427 /* More diagnostic information including link speed */
1428 if ( ! eb_pxenv_undi_get_iface_info() ) continue;
1429 printf ( "NDIS type %s interface at %d Mbps\n",
1430 undi.pxs->undi_get_iface_info.IfaceType,
1431 undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
1433 nic->nic_op = &undi_operations;
1436 undi_disable ( nic, pci ); /* To free base memory structures */
1440 /* UNDI driver states that it is suitable for any PCI NIC (i.e. any
1441 * PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any
1442 * obscure UNDI NICs that have the incorrect PCI class, add them to
1445 static struct pci_id undi_nics[] = {
1446 PCI_ROM ( 0x0000, 0x0000, "undi", "UNDI driver support" ),
1449 PCI_DRIVER ( undi_driver, undi_nics, PCI_CLASS_NETWORK_ETHERNET );
1451 DRIVER ( "UNDI", nic_driver, pci_driver, undi_driver,
1452 undi_probe, undi_disable );