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