Split PXE code into preboot, udp, tftp, undi and loader units.
authorMichael Brown <mcb30@etherboot.org>
Mon, 23 May 2005 23:32:56 +0000 (23:32 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 23 May 2005 23:32:56 +0000 (23:32 +0000)
PXE code now compiles without errors (though it won't actually work).

14 files changed:
src/arch/i386/image/pxe_image.c
src/arch/i386/include/pxe_addr.h
src/arch/i386/include/pxe_callbacks.h
src/core/pxe.c [deleted file]
src/include/pxe.h
src/include/pxe_api.h
src/include/pxe_export.h [deleted file]
src/include/pxe_types.h
src/interface/pxe/pxe.c [new file with mode: 0644]
src/interface/pxe/pxe_loader.c [new file with mode: 0644]
src/interface/pxe/pxe_preboot.c [new file with mode: 0644]
src/interface/pxe/pxe_tftp.c [new file with mode: 0644]
src/interface/pxe/pxe_udp.c
src/interface/pxe/pxe_undi.c [new file with mode: 0644]

index 1b61189..bd1bad3 100644 (file)
@@ -13,7 +13,6 @@
 
 #include "etherboot.h"
 #include "pxe_callbacks.h"
-#include "pxe_export.h"
 #include "pxe.h"
 
 unsigned long pxe_load_offset;
index b7cc6f8..954551e 100644 (file)
@@ -8,28 +8,10 @@
 #ifndef PXE_ADDR_H
 #define PXE_ADDR_H
 
-/* SEGOFF16_t defined in separate header
- */
-#include "realmode.h"
-typedef segoff_t I386_SEGOFF16_t;
-#define SEGOFF16_t I386_SEGOFF16_t
-
 #define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
 #define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
 #define PTR_TO_SEGOFF16(ptr,segoff16) \
        (segoff16).segment = SEGMENT(ptr); \
        (segoff16).offset  = OFFSET(ptr);
 
-typedef struct {
-       uint16_t                Seg_Addr;
-       uint32_t                Phy_Addr;
-       uint16_t                Seg_Size;
-} PACKED I386_SEGDESC_t;  /* PACKED is required, otherwise gcc pads
-                         * this out to 12 bytes -
-                         * mbrown@fensystems.co.uk (mcb30) 17/5/03 */
-#define SEGDESC_t I386_SEGDESC_t
-
-typedef        uint16_t I386_SEGSEL_t;
-#define SEGSEL_t I386_SEGSEL_t
-
 #endif /* PXE_ADDR_H */
index cf5a7a8..974a3c3 100644 (file)
@@ -5,12 +5,12 @@
 #define PXE_CALLBACKS_H
 
 #include "etherboot.h"
-#include "pxe.h"
+#include "pxe_types.h"
 
 typedef struct {
-       segoff_t        orig_retaddr;
-       uint16_t        opcode;
-       segoff_t        segoff;
+       SEGOFF16_t      orig_retaddr;
+       UINT16_t        opcode;
+       SEGOFF16_t      segoff;
 } PACKED pxe_call_params_t;
 
 /*
@@ -22,7 +22,7 @@ typedef struct {
 
 /* Function prototypes
  */
-extern pxe_stack_t * install_pxe_stack ( void *base );
+extern struct pxe_stack * install_pxe_stack ( void *base );
 extern void use_undi_ds_for_rm_stack ( uint16_t ds );
 extern int hook_pxe_stack ( void );
 extern int unhook_pxe_stack ( void );
diff --git a/src/core/pxe.c b/src/core/pxe.c
deleted file mode 100644 (file)
index 8b87894..0000000
+++ /dev/null
@@ -1,1445 +0,0 @@
-/* PXE API interface for Etherboot.
- *
- * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* Tags used in this file:
- *
- * FIXME : obvious
- * PXESPEC : question over interpretation of the PXE spec.
- */
-
-#ifdef PXE_EXPORT
-
-#include "etherboot.h"
-#include "pxe.h"
-#include "pxe_export.h"
-#include "pxe_callbacks.h"
-#include "dev.h"
-#include "nic.h"
-#include "pci.h"
-#include "cpu.h"
-#include "timer.h"
-
-#undef DBG
-#if TRACE_PXE
-#define DBG(...) printf ( __VA_ARGS__ )
-#else
-#define DBG(...)
-#endif
-
-/* Not sure why this isn't a globally-used structure within Etherboot.
- * (Because I didn't want to use packed to prevent gcc from aligning
- * source however it liked. Also nstype is a u16, not a uint. - Ken)
- */
-typedef        struct {
-       char dest[ETH_ALEN];
-       char source[ETH_ALEN];
-       unsigned int nstype;
-} media_header_t;
-static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
-
-/* Global pointer to currently installed PXE stack */
-pxe_stack_t *pxe_stack = NULL;
-
-/* Various startup/shutdown routines.  The startup/shutdown call
- * sequence is incredibly badly defined in the Intel PXE spec, for
- * example:
- *
- *   PXENV_UNDI_INITIALIZE says that the parameters used to initialize
- *   the adaptor should be those supplied to the most recent
- *   PXENV_UNDI_STARTUP call.  PXENV_UNDI_STARTUP takes no parameters.
- *
- *   PXENV_UNDI_CLEANUP says that the rest of the API will not be
- *   available after making this call.  Figure 3-3 ("Early UNDI API
- *   usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
- *   call to the supposedly now unavailable PXENV_STOP_UNDI.
- *
- *   PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
- *   occupied by the PXE stack.  Figure 4-3 ("PXE IPL") shows a call
- *   to PXENV_STOP_UNDI being made after the call to
- *   PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
- *   should have been freed (and, potentially, zeroed).
- *
- *   Nothing, anywhere, seems to mention who's responsible for freeing
- *   up the base memory allocated for the stack segment.  It's not
- *   even clear whether or not this is expected to be in free base
- *   memory rather than claimed base memory.
- *
- * Consequently, we adopt a rather defensive strategy, designed to
- * work with any conceivable sequence of initialisation or shutdown
- * calls.  We have only two things that we care about:
- *
- *   1. Have we hooked INT 1A and INT 15,E820(etc.)?
- *   2. Is the NIC initialised?
- *
- * The NIC should never be initialised without the vectors being
- * hooked, similarly the vectors should never be unhooked with the NIC
- * still initialised.  We do, however, want to be able to have the
- * vectors hooked with the NIC shutdown.  We therefore have three
- * possible states:
- *
- *   1. Ready to unload: interrupts unhooked, NIC shutdown.
- *   2. Midway: interrupts hooked, NIC shutdown.
- *   3. Fully ready: interrupts hooked, NIC initialised.
- *
- * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
- * these, and the call pxe_ensure_state() to ensure that the stack is
- * in the specified state.  All our PXE API call implementations
- * should use this call to ensure that the state is as required for
- * that PXE API call.  This enables us to cope with whatever the
- * end-user's interpretation of the PXE spec may be.  It even allows
- * for someone calling e.g. PXENV_START_UNDI followed by
- * PXENV_UDP_WRITE, without bothering with any of the intervening
- * calls.
- *
- * pxe_ensure_state() returns 1 for success, 0 for failure.  In the
- * event of failure (which can arise from e.g. asking for state READY
- * when we don't know where our NIC is), the error code
- * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
- * The macros ENSURE_XXX() can be used to achieve this without lots of
- * duplicated code.
- */
-
-/* pxe_[un]hook_stack are architecture-specific and provided in
- * pxe_callbacks.c
- */
-
-int pxe_initialise_nic ( void ) {
-       if ( pxe_stack->state >= READY ) return 1;
-
-#warning "device probing mechanism has completely changed"
-#if 0
-
-       /* Check if NIC is initialised.  dev.disable is set to 0
-        * when disable() is called, so we use this.
-        */
-       if ( dev.disable ) {
-               /* NIC may have been initialised independently
-                * (e.g. when we set up the stack prior to calling the
-                * NBP).
-                */
-               pxe_stack->state = READY;
-               return 1;
-       }
-
-       /* If we already have a NIC defined, reuse that one with
-        * PROBE_AWAKE.  If one was specifed via PXENV_START_UNDI, try
-        * that one first.  Otherwise, set PROBE_FIRST.
-        */
-
-       if ( dev.state.pci.dev.use_specified == 1 ) {
-               dev.how_probe = PROBE_NEXT;
-               DBG ( " initialising NIC specified via START_UNDI" );
-       } else if ( dev.state.pci.dev.driver ) {
-               DBG ( " reinitialising NIC" );
-               dev.how_probe = PROBE_AWAKE;
-       } else {
-               DBG ( " probing for any NIC" );
-               dev.how_probe = PROBE_FIRST;
-       }
-
-       /* Call probe routine to bring up the NIC */
-       if ( eth_probe ( &dev ) != PROBE_WORKED ) {
-               DBG ( " failed" );
-               return 0;
-       }
-#endif
-       
-
-       pxe_stack->state = READY;
-       return 1;
-}
-
-int pxe_shutdown_nic ( void ) {
-       if ( pxe_stack->state <= MIDWAY ) return 1;
-
-       eth_irq ( DISABLE );
-       disable ( &dev );
-       pxe_stack->state = MIDWAY;
-       return 1;
-}
-
-int ensure_pxe_state ( pxe_stack_state_t wanted ) {
-       int success = 1;
-
-       if ( ! pxe_stack ) return 0;
-       if ( wanted >= MIDWAY )
-               success = success & hook_pxe_stack();
-       if ( wanted > MIDWAY ) {
-               success = success & pxe_initialise_nic();
-       } else {
-               success = success & pxe_shutdown_nic();
-       }
-       if ( wanted < MIDWAY )
-               success = success & unhook_pxe_stack();
-       return success;
-}
-
-#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \
-                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
-                       return PXENV_EXIT_FAILURE; }
-#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \
-                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
-                       return PXENV_EXIT_FAILURE; }
-#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \
-                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
-                       return PXENV_EXIT_FAILURE; }
-
-/*****************************************************************************
- *
- * Actual PXE API calls
- *
- *****************************************************************************/
-
-/* PXENV_START_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI *start_undi ) {
-       unsigned char bus, devfn;
-
-       DBG ( "PXENV_START_UNDI" );
-       ENSURE_MIDWAY(start_undi);
-
-       /* Record PCI bus & devfn passed by caller, so we know which
-        * NIC they want to use.
-        *
-        * If they don't match our already-existing NIC structure, set
-        * values to ensure that the specified NIC is used at the next
-        * call to pxe_intialise_nic().
-        */
-       bus = ( start_undi->ax >> 8 ) & 0xff;
-       devfn = start_undi->ax & 0xff;
-
-#warning "device probing mechanism has completely changed"
-#if 0
-       if ( ( pci->dev.driver == NULL ) ||
-            ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
-               /* This is quite a bit of a hack and relies on
-                * knowledge of the internal operation of Etherboot's
-                * probe mechanism.
-                */
-               DBG ( " set PCI %hhx:%hhx.%hhx",
-                     bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
-               dev->type = BOOT_NIC;
-               dev->to_probe = PROBE_PCI;
-               memset ( &dev->state, 0, sizeof(dev->state) );
-               pci->advance = 1;
-               pci->dev.use_specified = 1;
-               pci->dev.bus = bus;
-               pci->dev.devfn = devfn;
-       }
-#endif
-
-       start_undi->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_STARTUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP *undi_startup ) {
-       DBG ( "PXENV_UNDI_STARTUP" );
-       ENSURE_MIDWAY(undi_startup);
-
-       undi_startup->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLEANUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP *undi_cleanup ) {
-       DBG ( "PXENV_UNDI_CLEANUP" );
-       ENSURE_CAN_UNLOAD ( undi_cleanup );
-
-       undi_cleanup->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_INITIALIZE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE
-                                    *undi_initialize ) {
-       DBG ( "PXENV_UNDI_INITIALIZE" );
-       ENSURE_MIDWAY ( undi_initialize );
-
-       undi_initialize->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_RESET_ADAPTER
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_reset_adapter ( t_PXENV_UNDI_RESET_ADAPTER
-                                       *undi_reset_adapter ) {
-       DBG ( "PXENV_UNDI_RESET_ADAPTER" );
-
-       ENSURE_MIDWAY ( undi_reset_adapter );
-       ENSURE_READY ( undi_reset_adapter );
-
-       undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SHUTDOWN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
-       DBG ( "PXENV_UNDI_SHUTDOWN" );
-       ENSURE_MIDWAY ( undi_shutdown );
-
-       undi_shutdown->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN *undi_open ) {
-       DBG ( "PXENV_UNDI_OPEN" );
-       ENSURE_READY ( undi_open );
-
-       /* PXESPEC: This is where we choose to enable interrupts.
-        * Can't actually find where we're meant to in the PXE spec,
-        * but this should work.
-        */
-       eth_irq ( ENABLE );
-
-       undi_open->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE *undi_close ) {
-       DBG ( "PXENV_UNDI_CLOSE" );
-       ENSURE_MIDWAY ( undi_close );
-
-       undi_close->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_TRANSMIT
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT *undi_transmit ) {
-       t_PXENV_UNDI_TBD *tbd;
-       const char *dest;
-       unsigned int type;
-       unsigned int length;
-       const char *data;
-       media_header_t *media_header;
-
-       DBG ( "PXENV_UNDI_TRANSMIT" );
-       ENSURE_READY ( undi_transmit );
-
-       /* We support only the "immediate" portion of the TBD.  Who
-        * knows what Intel's "engineers" were smoking when they came
-        * up with the array of transmit data blocks...
-        */
-       tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
-       if ( tbd->DataBlkCount > 0 ) {
-               undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
-               return PXENV_EXIT_FAILURE;
-       }
-       data = SEGOFF16_TO_PTR ( tbd->Xmit );
-       length = tbd->ImmedLength;
-
-       /* If destination is broadcast, we need to supply the MAC address */
-       if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
-               dest = broadcast_mac;
-       } else {
-               dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
-       }
-
-       /* We can't properly support P_UNKNOWN without rewriting all
-        * the driver transmit() methods, so we cheat: if P_UNKNOWN is
-        * specified we rip the destination address and type out of
-        * the pre-assembled packet, then skip over the header.
-        */
-       switch ( undi_transmit->Protocol ) {
-       case P_IP:      type = IP;      break;
-       case P_ARP:     type = ARP;     break;
-       case P_RARP:    type = RARP;    break;
-       case P_UNKNOWN:
-               media_header = (media_header_t*)data;
-               dest = media_header->dest;
-               type = ntohs ( media_header->nstype );
-               data += ETH_HLEN;
-               length -= ETH_HLEN;
-               break;
-       default:
-               undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
-               return PXENV_EXIT_FAILURE;
-       }
-
-       /* Send the packet */
-       eth_transmit ( dest, type, length, data );
-       
-       undi_transmit->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SET_MCAST_ADDRESS
- *
- * Status: stub (no PXE multicast support)
- */
-PXENV_EXIT_t pxenv_undi_set_mcast_address ( t_PXENV_UNDI_SET_MCAST_ADDRESS
-                                           *undi_set_mcast_address ) {
-       DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
-       /* ENSURE_READY ( undi_set_mcast_address ); */
-       undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_SET_STATION_ADDRESS
- *
- * Status: working (deliberately incomplete)
- */
-PXENV_EXIT_t pxenv_undi_set_station_address ( t_PXENV_UNDI_SET_STATION_ADDRESS
-                                             *undi_set_station_address ) {
-       DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
-       ENSURE_READY ( undi_set_station_address );
-
-       /* We don't offer a facility to set the MAC address; this
-        * would require adding extra code to all the Etherboot
-        * drivers, for very little benefit.  If we're setting it to
-        * the current value anyway then return success, otherwise
-        * return UNSUPPORTED.
-        */
-       if ( memcmp ( nic.node_addr,
-                     &undi_set_station_address->StationAddress,
-                     ETH_ALEN ) == 0 ) {
-               undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
-               return PXENV_EXIT_SUCCESS;
-       }
-       undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_SET_PACKET_FILTER
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_set_packet_filter ( t_PXENV_UNDI_SET_PACKET_FILTER
-                                           *undi_set_packet_filter ) {
-       DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
-       /* ENSURE_READY ( undi_set_packet_filter ); */
-       undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_GET_INFORMATION
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_information ( t_PXENV_UNDI_GET_INFORMATION
-                                         *undi_get_information ) {
-       DBG ( "PXENV_UNDI_GET_INFORMATION" );
-       ENSURE_READY ( undi_get_information );
-
-       undi_get_information->BaseIo = nic.ioaddr;
-       undi_get_information->IntNumber = nic.irqno;
-       /* Cheat: assume all cards can cope with this */
-       undi_get_information->MaxTranUnit = ETH_MAX_MTU;
-       /* Cheat: we only ever have Ethernet cards */
-       undi_get_information->HwType = ETHER_TYPE;
-       undi_get_information->HwAddrLen = ETH_ALEN;
-       /* Cheat: assume card is always configured with its permanent
-        * node address.  This is a valid assumption within Etherboot
-        * at the time of writing.
-        */
-       memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
-                ETH_ALEN );
-       memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
-                ETH_ALEN );
-       undi_get_information->ROMAddress = 0;
-               /* nic.rom_info->rom_segment; */
-       /* We only provide the ability to receive or transmit a single
-        * packet at a time.  This is a bootloader, not an OS.
-        */
-       undi_get_information->RxBufCt = 1;
-       undi_get_information->TxBufCt = 1;
-       undi_get_information->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_STATISTICS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS
-                                        *undi_get_statistics ) {
-       DBG ( "PXENV_UNDI_GET_STATISTICS" );
-       /* ENSURE_READY ( undi_get_statistics ); */
-       undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_CLEAR_STATISTICS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_clear_statistics ( t_PXENV_UNDI_CLEAR_STATISTICS
-                                          *undi_clear_statistics ) {
-       DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
-       /* ENSURE_READY ( undi_clear_statistics ); */
-       undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_INITIATE_DIAGS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS
-                                        *undi_initiate_diags ) {
-       DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
-       /* ENSURE_READY ( undi_initiate_diags ); */
-       undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_FORCE_INTERRUPT
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_force_interrupt ( t_PXENV_UNDI_FORCE_INTERRUPT
-                                         *undi_force_interrupt ) {
-       DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
-       ENSURE_READY ( undi_force_interrupt );
-
-       eth_irq ( FORCE );
-       undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_MCAST_ADDRESS
- *
- * Status: stub (no PXE multicast support)
- */
-PXENV_EXIT_t pxenv_undi_get_mcast_address ( t_PXENV_UNDI_GET_MCAST_ADDRESS
-                                           *undi_get_mcast_address ) {
-       DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
-       /* ENSURE_READY ( undi_get_mcast_address ); */
-       undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_GET_NIC_TYPE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_nic_type ( t_PXENV_UNDI_GET_NIC_TYPE
-                                      *undi_get_nic_type ) {
-#warning "device probing mechanism has changed completely"
-
-#if 0
-       struct dev *dev = &dev;
-       
-       DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
-       ENSURE_READY ( undi_get_nic_type );
-       
-       if ( dev->to_probe == PROBE_PCI ) {
-               struct pci_device *pci = &dev->state.pci.dev;
-
-               undi_get_nic_type->NicType = PCI_NIC;
-               undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
-               undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
-               undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
-               undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
-               undi_get_nic_type->info.pci.BusDevFunc =
-                       ( pci->bus << 8 ) | pci->devfn;
-               /* Cheat: these fields are probably unnecessary, and
-                * would require adding extra code to pci.c.
-                */
-               undi_get_nic_type->info.pci.Prog_Intf = 0;
-               undi_get_nic_type->info.pci.Rev = 0;
-               undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
-               undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
-       } else if ( dev->to_probe == PROBE_ISA ) {
-               /* const struct isa_driver *isa = dev->state.isa.driver; */
-
-               undi_get_nic_type->NicType = PnP_NIC;
-               /* Don't think anything fills these fields in, and
-                * probably no-one will ever be interested in them.
-                */
-               undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
-               undi_get_nic_type->info.pnp.Base_Class = 0;
-               undi_get_nic_type->info.pnp.Sub_Class = 0;
-               undi_get_nic_type->info.pnp.Prog_Intf = 0;
-               undi_get_nic_type->info.pnp.CardSelNum = 0;
-       } else {
-               /* PXESPEC: There doesn't seem to be an "unknown type"
-                * defined.
-                */
-               undi_get_nic_type->NicType = 0;
-       }
-       undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-
-#endif
-}
-
-/* PXENV_UNDI_GET_IFACE_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO
-                                        *undi_get_iface_info ) {
-       DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
-       ENSURE_READY ( undi_get_iface_info );
-
-       /* Just hand back some info, doesn't really matter what it is.
-        * Most PXE stacks seem to take this approach.
-        */
-       sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
-       undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
-       undi_get_iface_info->ServiceFlags = 0;
-       memset ( undi_get_iface_info->Reserved, 0,
-                sizeof(undi_get_iface_info->Reserved) );
-       undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_ISR
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) {
-       media_header_t *media_header = (media_header_t*)nic.packet;
-
-       DBG ( "PXENV_UNDI_ISR" );
-       /* We can't call ENSURE_READY, because this could be being
-        * called as part of an interrupt service routine.  Instead,
-        * we should simply die if we're not READY.
-        */
-       if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
-               undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
-               return PXENV_EXIT_FAILURE;
-       }
-       
-       /* Just in case some idiot actually looks at these fields when
-        * we weren't meant to fill them in...
-        */
-       undi_isr->BufferLength = 0;
-       undi_isr->FrameLength = 0;
-       undi_isr->FrameHeaderLength = 0;
-       undi_isr->ProtType = 0;
-       undi_isr->PktType = 0;
-
-       switch ( undi_isr->FuncFlag ) {
-       case PXENV_UNDI_ISR_IN_START :
-               /* Is there a packet waiting?  If so, disable
-                * interrupts on the NIC and return "it's ours".  Do
-                * *not* necessarily acknowledge the interrupt; this
-                * can happen later when eth_poll(1) is called.  As
-                * long as the interrupt is masked off so that it
-                * doesn't immediately retrigger the 8259A then all
-                * should be well.
-                */
-               DBG ( " START" );
-               if ( eth_poll ( 0 ) ) {
-                       DBG ( " OURS" );
-                       eth_irq ( DISABLE );
-                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
-               } else {
-                       DBG ( " NOT_OURS" );
-                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
-               }
-               break;
-       case PXENV_UNDI_ISR_IN_PROCESS :
-               /* Call poll(), return packet.  If no packet, return "done".
-                */
-               DBG ( " PROCESS" );
-               if ( eth_poll ( 1 ) ) {
-                       DBG ( " RECEIVE %d", nic.packetlen );
-                       if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
-                               /* Should never happen */
-                               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
-                               undi_isr->Status =
-                                       PXENV_STATUS_OUT_OF_RESOURCES;
-                               return PXENV_EXIT_FAILURE;
-                       }
-                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
-                       undi_isr->BufferLength = nic.packetlen;
-                       undi_isr->FrameLength = nic.packetlen;
-                       undi_isr->FrameHeaderLength = ETH_HLEN;
-                       memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
-                       PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
-                       switch ( ntohs(media_header->nstype) ) {
-                       case IP :       undi_isr->ProtType = P_IP;      break;
-                       case ARP :      undi_isr->ProtType = P_ARP;     break;
-                       case RARP :     undi_isr->ProtType = P_RARP;    break;
-                       default :       undi_isr->ProtType = P_UNKNOWN;
-                       }
-                       if ( memcmp ( media_header->dest, broadcast_mac,
-                                     sizeof(broadcast_mac) ) ) {
-                               undi_isr->PktType = XMT_BROADCAST;
-                       } else {
-                               undi_isr->PktType = XMT_DESTADDR;
-                       }
-                       break;
-               } else {
-                       /* No break - fall through to IN_GET_NEXT */
-               }
-       case PXENV_UNDI_ISR_IN_GET_NEXT :
-               /* We only ever return one frame at a time */
-               DBG ( " GET_NEXT DONE" );
-               /* Re-enable interrupts */
-               eth_irq ( ENABLE );
-               /* Force an interrupt if there's a packet still
-                * waiting, since we only handle one packet per
-                * interrupt.
-                */
-               if ( eth_poll ( 0 ) ) {
-                       DBG ( " (RETRIGGER)" );
-                       eth_irq ( FORCE );
-               }
-               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
-               break;
-       default :
-               /* Should never happen */
-               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
-               undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
-               return PXENV_EXIT_FAILURE;
-       }
-
-       undi_isr->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_STOP_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI *stop_undi ) {
-       DBG ( "PXENV_STOP_UNDI" );
-
-       if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
-               stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
-               return PXENV_EXIT_FAILURE;
-       }
-
-       stop_undi->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN *tftp_open ) {
-       struct sockaddr_in tftp_server;
-       struct tftpreq_info_t request;
-       struct tftpblk_info_t block;
-
-       DBG ( "PXENV_TFTP_OPEN" );
-       ENSURE_READY ( tftp_open );
-
-       /* Set server address and port */
-       tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
-               ? tftp_open->ServerIPAddress
-               : arptable[ARP_SERVER].ipaddr.s_addr;
-       tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
-#ifdef WORK_AROUND_BPBATCH_BUG        
-       /* Force use of port 69; BpBatch tries to use port 4 for some         
-       * bizarre reason.         */        
-       tftp_server.sin_port = TFTP_PORT;
-#endif
-       /* Ignore gateway address; we can route properly */
-       /* Fill in request structure */
-       request.server = &tftp_server;
-       request.name = tftp_open->FileName;
-       request.blksize = tftp_open->PacketSize;
-       DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
-             tftp_open->TFTPPort, request.name, request.blksize );
-       if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
-       /* Make request and get first packet */
-       if ( !tftp_block ( &request, &block ) ) {
-               tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
-               return PXENV_EXIT_FAILURE;
-       }
-       /* Fill in PacketSize */
-       tftp_open->PacketSize = request.blksize;
-       /* Store first block for later retrieval by TFTP_READ */
-       pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
-       pxe_stack->tftpdata.len = block.len;
-       pxe_stack->tftpdata.eof = block.eof;
-       memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
-
-       tftp_open->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE *tftp_close ) {
-       DBG ( "PXENV_TFTP_CLOSE" );
-       ENSURE_READY ( tftp_close );
-       tftp_close->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_READ
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ *tftp_read ) {
-       struct tftpblk_info_t block;
-
-       DBG ( "PXENV_TFTP_READ" );
-       ENSURE_READY ( tftp_read );
-
-       /* Do we have a block pending */
-       if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
-               block.data = pxe_stack->tftpdata.data;
-               block.len = pxe_stack->tftpdata.len;
-               block.eof = pxe_stack->tftpdata.eof;
-               block.block = 1; /* Will be the first block */
-               pxe_stack->tftpdata.magic_cookie = 0;
-       } else {
-               if ( !tftp_block ( NULL, &block ) ) {
-                       tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
-                       return PXENV_EXIT_FAILURE;
-               }
-       }
-
-       /* Return data */
-       tftp_read->PacketNumber = block.block;
-       tftp_read->BufferSize = block.len;
-       memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
-       DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
-             tftp_read->Buffer.offset );
-       tftp_read->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_READ_FILE
- *
- * Status: working
- */
-
-int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused,
-                         unsigned int len, int eof ) {
-       if ( pxe_stack->readfile.buffer ) {
-               if ( pxe_stack->readfile.offset + len >=
-                    pxe_stack->readfile.bufferlen ) return -1;
-               memcpy ( pxe_stack->readfile.buffer +
-                        pxe_stack->readfile.offset, data, len );
-       }
-       pxe_stack->readfile.offset += len;
-       return eof ? 0 : 1;
-}
-
-PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE *tftp_read_file ) {
-       struct sockaddr_in tftp_server;
-       int rc;
-
-       DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
-             tftp_read_file->Buffer,
-             tftp_read_file->Buffer + tftp_read_file->BufferSize );
-       ENSURE_READY ( tftp_read_file );
-
-       /* inserted by Klaus Wittemeier */
-       /* KERNEL_BUF stores the name of the last required file */
-       /* This is a fix to make Microsoft Remote Install Services work (RIS) */
-       memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
-       /* end of insertion */
-
-       /* Set server address and port */
-       tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
-               ? tftp_read_file->ServerIPAddress
-               : arptable[ARP_SERVER].ipaddr.s_addr;
-       tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
-
-       pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
-       pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
-       pxe_stack->readfile.offset = 0;
-
-       rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
-                   pxe_tftp_read_block );
-       if ( rc ) {
-               tftp_read_file->Status = PXENV_STATUS_FAILURE;
-               return PXENV_EXIT_FAILURE;
-       }
-       tftp_read_file->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_GET_FSIZE
- *
- * Status: working, though ugly (we actually read the whole file,
- * because it's too ugly to make Etherboot request the tsize option
- * and hand it to us).
- */
-PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE *tftp_get_fsize ) {
-       int rc;
-
-       DBG ( "PXENV_TFTP_GET_FSIZE" );
-       ENSURE_READY ( tftp_get_fsize );
-
-       pxe_stack->readfile.buffer = NULL;
-       pxe_stack->readfile.bufferlen = 0;
-       pxe_stack->readfile.offset = 0;
-
-#warning "Rewrite pxenv_tftp_get_fsize, please"
-       if ( rc ) {
-               tftp_get_fsize->FileSize = 0;
-               tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
-               return PXENV_EXIT_FAILURE;
-       }
-       tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
-       tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN *udp_open ) {
-       DBG ( "PXENV_UDP_OPEN" );
-       ENSURE_READY ( udp_open );
-
-       if ( udp_open->src_ip &&
-            udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
-               /* Overwrite our IP address */
-               DBG ( " with new IP %@", udp_open->src_ip );
-               arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
-       }
-
-       udp_open->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE *udp_close ) {
-       DBG ( "PXENV_UDP_CLOSE" );
-       ENSURE_READY ( udp_close );
-       udp_close->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_READ
- *
- * Status: working
- */
-int await_pxe_udp ( int ival __unused, void *ptr,
-                   unsigned short ptype __unused,
-                   struct iphdr *ip, struct udphdr *udp,
-                   struct tcphdr *tcp __unused ) {
-       t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr;
-       uint16_t d_port;
-       size_t size;
-
-       /* Ignore non-UDP packets */
-       if ( !udp ) {
-               DBG ( " non-UDP" );
-               return 0;
-       }
-       
-       /* Check dest_ip */
-       if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
-               DBG ( " wrong dest IP (got %@, wanted %@)",
-                     ip->dest.s_addr, udp_read->dest_ip );
-               return 0;
-       }
-
-       /* Check dest_port */
-       d_port = ntohs ( udp_read->d_port );
-       if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
-               DBG ( " wrong dest port (got %d, wanted %d)",
-                     ntohs(udp->dest), d_port );
-               return 0;
-       }
-
-       /* Copy packet to buffer and fill in information */
-       udp_read->src_ip = ip->src.s_addr;
-       udp_read->s_port = udp->src; /* Both in network order */
-       size = ntohs(udp->len) - sizeof(*udp);
-       /* Workaround: NTLDR expects us to fill these in, even though
-        * PXESPEC clearly defines them as input parameters.
-        */
-       udp_read->dest_ip = ip->dest.s_addr;
-       udp_read->d_port = udp->dest;
-       DBG ( " %@:%d->%@:%d (%d)",
-             udp_read->src_ip, ntohs(udp_read->s_port),
-             udp_read->dest_ip, ntohs(udp_read->d_port), size );
-       if ( udp_read->buffer_size < size ) {
-               /* PXESPEC: what error code should we actually return? */
-               DBG ( " buffer too small (%d)", udp_read->buffer_size );
-               udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
-               return 0;
-       }
-       memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
-       udp_read->buffer_size = size;
-
-       return 1;
-}
-
-PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ *udp_read ) {
-       DBG ( "PXENV_UDP_READ" );
-       ENSURE_READY ( udp_read );
-
-       /* Use await_reply with a timeout of zero */
-       /* Allow await_reply to change Status if necessary */
-       udp_read->Status = PXENV_STATUS_FAILURE;
-       if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
-               return PXENV_EXIT_FAILURE;
-       }
-
-       udp_read->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_WRITE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE *udp_write ) {
-       uint16_t src_port;
-       uint16_t dst_port;
-       struct udppacket *packet = (struct udppacket *)nic.packet;
-       int packet_size;
-
-       DBG ( "PXENV_UDP_WRITE" );
-       ENSURE_READY ( udp_write );
-
-       /* PXE spec says source port is 2069 if not specified */
-       src_port = ntohs(udp_write->src_port);
-       if ( src_port == 0 ) src_port = 2069;
-       dst_port = ntohs(udp_write->dst_port);
-       DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
-             udp_write->buffer_size );
-       
-       /* FIXME: we ignore the gateway specified, since we're
-        * confident of being able to do our own routing.  We should
-        * probably allow for multiple gateways.
-        */
-       
-       /* Copy payload to packet buffer */
-       packet_size = ( (void*)&packet->payload - (void*)packet )
-               + udp_write->buffer_size;
-       if ( packet_size > ETH_FRAME_LEN ) {
-               udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
-               return PXENV_EXIT_FAILURE;
-       }
-       memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
-                udp_write->buffer_size );
-
-       /* Transmit packet */
-       if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
-                             packet_size, packet ) ) {
-               udp_write->Status = PXENV_STATUS_FAILURE;
-               return PXENV_EXIT_FAILURE;
-       }
-
-       udp_write->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNLOAD_STACK
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK *unload_stack ) {
-       int success;
-
-       DBG ( "PXENV_UNLOAD_STACK" );
-       success = ensure_pxe_state ( CAN_UNLOAD );
-
-       /* We need to call cleanup() at some point.  The network card
-        * has already been disabled by ENSURE_CAN_UNLOAD(), but for
-        * the sake of completeness we should call the console_fini()
-        * etc. that are part of cleanup().
-        *
-        * There seems to be a lack of consensus on which is the final
-        * PXE API call to make, but it's a fairly safe bet that all
-        * the potential shutdown sequences will include a call to
-        * PXENV_UNLOAD_STACK at some point, so we may as well do it
-        * here.
-        */
-       cleanup();
-
-       if ( ! success ) {
-               unload_stack->Status = PXENV_STATUS_KEEP_ALL;
-               return PXENV_EXIT_FAILURE;
-       }
-
-       unload_stack->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_GET_CACHED_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO
-                                    *get_cached_info ) {
-       BOOTPLAYER *cached_info = &pxe_stack->cached_info;
-       DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
-       ENSURE_READY ( get_cached_info );
-
-       /* Fill in cached_info structure in our pxe_stack */
-
-       /* I don't think there's actually any way we can be called in
-        * the middle of a DHCP request... 
-        */
-       cached_info->opcode = BOOTP_REP;
-       /* We only have Ethernet drivers */
-       cached_info->Hardware = ETHER_TYPE;
-       cached_info->Hardlen = ETH_ALEN;
-       /* PXESPEC: "Client sets" says the spec, but who's filling in
-        * this structure?  It ain't the client.
-        */
-       cached_info->Gatehops = 0;
-       cached_info->ident = 0;
-       cached_info->seconds = 0;
-       cached_info->Flags = BOOTP_BCAST;
-       /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
-       cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
-       cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
-       cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
-       /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
-       cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
-       memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
-       /* Nullify server name */
-       cached_info->Sname[0] = '\0';
-       memcpy ( cached_info->bootfile, KERNEL_BUF,
-                sizeof(cached_info->bootfile) );
-       /* Copy DHCP vendor options */
-       memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
-                sizeof(cached_info->vendor.d) );
-       
-       /* Copy to user-specified buffer, or set pointer to our buffer */
-       get_cached_info->BufferLimit = sizeof(*cached_info);
-       /* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
-        * 0, but what are we supposed to do with a null buffer of
-        * non-zero size?!
-        */
-       if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
-               /* Point back to our buffer */
-               PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
-               get_cached_info->BufferSize = sizeof(*cached_info);
-       } else {
-               /* Copy to user buffer */
-               size_t size = sizeof(*cached_info);
-               void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
-               if ( get_cached_info->BufferSize < size )
-                       size = get_cached_info->BufferSize;
-               DBG ( " to %x", virt_to_phys ( buffer ) );
-               memcpy ( buffer, cached_info, size );
-               /* PXESPEC: Should we return an error if the user
-                * buffer is too small?  We do return the actual size
-                * of the buffer via BufferLimit, so the user does
-                * have a way to detect this already.
-                */
-       }
-
-       get_cached_info->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_RESTART_TFTP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP *restart_tftp ) {
-       PXENV_EXIT_t tftp_exit;
-
-       DBG ( "PXENV_RESTART_TFTP" );
-       ENSURE_READY ( restart_tftp );
-
-       /* Words cannot describe the complete mismatch between the PXE
-        * specification and any possible version of reality...
-        */
-       restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
-       restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
-       DBG ( "(" );
-       tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (t_PXENV_ANY*)restart_tftp );
-       DBG ( ")" );
-       if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
-
-       /* Fire up the new NBP */
-       restart_tftp->Status = xstartpxe();
-
-       /* Not sure what "SUCCESS" actually means, since we can only
-        * return if the new NBP failed to boot...
-        */
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_START_BASE
- *
- * Status: won't implement (requires major structural changes)
- */
-PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE *start_base ) {
-       DBG ( "PXENV_START_BASE" );
-       /* ENSURE_READY ( start_base ); */
-       start_base->Status = PXENV_STATUS_UNSUPPORTED;
-       return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_STOP_BASE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_base ( t_PXENV_STOP_BASE *stop_base ) {
-       DBG ( "PXENV_STOP_BASE" );
-
-       /* The only time we will be called is when the NBP is trying
-        * to shut down the PXE stack.  There's nothing we need to do
-        * in this call.
-        */
-
-       stop_base->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_LOADER
- *
- * Status: working
- *
- * NOTE: This is not a genuine PXE API call; the loader has a separate
- * entry point.  However, to simplify the mapping of the PXE API to
- * the internal Etherboot API, both are directed through the same
- * interface.
- */
-PXENV_EXIT_t pxenv_undi_loader ( undi_loader_t *loader ) {
-       uint32_t loader_phys = virt_to_phys ( loader );
-
-       DBG ( "PXENV_UNDI_LOADER" );
-       
-       /* Set UNDI DS as our real-mode stack */
-       use_undi_ds_for_rm_stack ( loader->undi_ds );
-
-       /* FIXME: These lines are borrowed from main.c.  There should
-        * probably be a single initialise() function that does all
-        * this, but it's currently split interestingly between main()
-        * and main_loop()...
-        */
-
-
-       /* CHECKME: Our init functions have probably already been
-          called by the ROM prefix's call to setup(), haven't
-          they? */
-
-
-
-       /* We have relocated; the loader pointer is now invalid */
-       loader = phys_to_virt ( loader_phys );
-
-       /* Install PXE stack to area specified by NBP */
-       install_pxe_stack ( VIRTUAL ( loader->undi_cs, 0 ) );
-       
-       /* Call pxenv_start_undi to set parameters.  Why the hell PXE
-        * requires these parameters to be provided twice is beyond
-        * the wit of any sane man.  Don't worry if it fails; the NBP
-        * should call PXENV_START_UNDI separately anyway.
-        */
-       pxenv_start_undi ( &loader->start_undi );
-       /* Unhook stack; the loader is not meant to hook int 1a etc,
-        * but the call the pxenv_start_undi will cause it to happen.
-        */
-       ENSURE_CAN_UNLOAD ( loader );
-
-       /* Fill in addresses of !PXE and PXENV+ structures */
-       PTR_TO_SEGOFF16 ( &pxe_stack->pxe, loader->pxe_ptr );
-       PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, loader->pxenv_ptr );
-       
-       loader->Status = PXENV_STATUS_SUCCESS;
-       return PXENV_EXIT_SUCCESS;
-}
-
-/* API call dispatcher
- *
- * Status: complete
- */
-PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params ) {
-       PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
-
-       /* Set default status in case child routine fails to do so */
-       params->Status = PXENV_STATUS_FAILURE;
-
-       DBG ( "[" );
-
-       /* Hand off to relevant API routine */
-       switch ( opcode ) {
-       case PXENV_START_UNDI:
-               ret = pxenv_start_undi ( &params->start_undi );
-               break;
-       case PXENV_UNDI_STARTUP:
-               ret = pxenv_undi_startup ( &params->undi_startup );
-               break;
-       case PXENV_UNDI_CLEANUP:
-               ret = pxenv_undi_cleanup ( &params->undi_cleanup );
-               break;
-       case PXENV_UNDI_INITIALIZE:
-               ret = pxenv_undi_initialize ( &params->undi_initialize );
-               break;
-       case PXENV_UNDI_RESET_ADAPTER:
-               ret = pxenv_undi_reset_adapter ( &params->undi_reset_adapter );
-               break;
-       case PXENV_UNDI_SHUTDOWN:
-               ret = pxenv_undi_shutdown ( &params->undi_shutdown );
-               break;
-       case PXENV_UNDI_OPEN:
-               ret = pxenv_undi_open ( &params->undi_open );
-               break;
-       case PXENV_UNDI_CLOSE:
-               ret = pxenv_undi_close ( &params->undi_close );
-               break;
-       case PXENV_UNDI_TRANSMIT:
-               ret = pxenv_undi_transmit ( &params->undi_transmit );
-               break;
-       case PXENV_UNDI_SET_MCAST_ADDRESS:
-               ret = pxenv_undi_set_mcast_address (
-                                            &params->undi_set_mcast_address );
-               break;
-       case PXENV_UNDI_SET_STATION_ADDRESS:
-               ret = pxenv_undi_set_station_address (
-                                          &params->undi_set_station_address );
-               break;
-       case PXENV_UNDI_SET_PACKET_FILTER:
-               ret = pxenv_undi_set_packet_filter (
-                                            &params->undi_set_packet_filter );
-               break;
-       case PXENV_UNDI_GET_INFORMATION:
-               ret = pxenv_undi_get_information (
-                                              &params->undi_get_information );
-               break;
-       case PXENV_UNDI_GET_STATISTICS:
-               ret = pxenv_undi_get_statistics (
-                                               &params->undi_get_statistics );
-               break;
-       case PXENV_UNDI_CLEAR_STATISTICS:
-               ret = pxenv_undi_clear_statistics (
-                                             &params->undi_clear_statistics );
-               break;
-       case PXENV_UNDI_INITIATE_DIAGS:
-               ret = pxenv_undi_initiate_diags (
-                                               &params->undi_initiate_diags );
-               break;
-       case PXENV_UNDI_FORCE_INTERRUPT:
-               ret = pxenv_undi_force_interrupt (
-                                              &params->undi_force_interrupt );
-               break;
-       case PXENV_UNDI_GET_MCAST_ADDRESS:
-               ret = pxenv_undi_get_mcast_address (
-                                            &params->undi_get_mcast_address );
-               break;
-       case PXENV_UNDI_GET_NIC_TYPE:
-               ret = pxenv_undi_get_nic_type ( &params->undi_get_nic_type );
-               break;
-       case PXENV_UNDI_GET_IFACE_INFO:
-               ret = pxenv_undi_get_iface_info (
-                                               &params->undi_get_iface_info );
-               break;
-       case PXENV_UNDI_ISR:
-               ret = pxenv_undi_isr ( &params->undi_isr );
-               break;
-       case PXENV_STOP_UNDI:
-               ret = pxenv_stop_undi ( &params->stop_undi );
-               break;
-       case PXENV_TFTP_OPEN:
-               ret = pxenv_tftp_open ( &params->tftp_open );
-               break;
-       case PXENV_TFTP_CLOSE:
-               ret = pxenv_tftp_close ( &params->tftp_close );
-               break;
-       case PXENV_TFTP_READ:
-               ret = pxenv_tftp_read ( &params->tftp_read );
-               break;
-       case PXENV_TFTP_READ_FILE:
-               ret = pxenv_tftp_read_file ( &params->tftp_read_file );
-               break;
-       case PXENV_TFTP_GET_FSIZE:
-               ret = pxenv_tftp_get_fsize ( &params->tftp_get_fsize );
-               break;
-       case PXENV_UDP_OPEN:
-               ret = pxenv_udp_open ( &params->udp_open );
-               break;
-       case PXENV_UDP_CLOSE:
-               ret = pxenv_udp_close ( &params->udp_close );
-               break;
-       case PXENV_UDP_READ:
-               ret = pxenv_udp_read ( &params->udp_read );
-               break;
-       case PXENV_UDP_WRITE:
-               ret = pxenv_udp_write ( &params->udp_write );
-               break;
-       case PXENV_UNLOAD_STACK:
-               ret = pxenv_unload_stack ( &params->unload_stack );
-               break;
-       case PXENV_GET_CACHED_INFO:
-               ret = pxenv_get_cached_info ( &params->get_cached_info );
-               break;
-       case PXENV_RESTART_TFTP:
-               ret = pxenv_restart_tftp ( &params->restart_tftp );
-               break;
-       case PXENV_START_BASE:
-               ret = pxenv_start_base ( &params->start_base );
-               break;
-       case PXENV_STOP_BASE:
-               ret = pxenv_stop_base ( &params->stop_base );
-               break;
-       case PXENV_UNDI_LOADER:
-               ret = pxenv_undi_loader ( &params->loader );
-               break;
-               
-       default:
-               DBG ( "PXENV_UNKNOWN_%hx", opcode );
-               params->Status = PXENV_STATUS_UNSUPPORTED;
-               ret = PXENV_EXIT_FAILURE;
-               break;
-       }
-
-       if ( params->Status != PXENV_STATUS_SUCCESS ) {
-               DBG ( " %hx", params->Status );
-       }
-       if ( ret != PXENV_EXIT_SUCCESS ) {
-               DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
-       }
-       DBG ( "]" );
-
-       return ret;
-}
-
-#endif /* PXE_EXPORT */
index d4c683f..3264df0 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "pxe_types.h"
 #include "pxe_api.h"
+#include "etherboot.h"
 
 /* Union used for PXE API calls; we don't know the type of the
  * structure until we interpret the opcode.  Also, Status is available
@@ -14,7 +15,7 @@ union u_PXENV_ANY {
        PXENV_STATUS_t                          Status;
        struct s_PXENV_UNLOAD_STACK             unload_stack;
        struct s_PXENV_GET_CACHED_INFO          get_cached_info;
-       struct s_PXENV_RESTART_TFTP             restart_tftp;
+       struct s_PXENV_TFTP_READ_FILE           restart_tftp;
        struct s_PXENV_START_UNDI               start_undi;
        struct s_PXENV_STOP_UNDI                stop_undi;
        struct s_PXENV_START_BASE               start_base;
@@ -31,7 +32,7 @@ union u_PXENV_ANY {
        struct s_PXENV_UNDI_STARTUP             undi_startup;
        struct s_PXENV_UNDI_CLEANUP             undi_cleanup;
        struct s_PXENV_UNDI_INITIALIZE          undi_initialize;
-       struct s_PXENV_UNDI_RESET_ADAPTER       undi_reset_adapter;
+       struct s_PXENV_UNDI_RESET               undi_reset_adapter;
        struct s_PXENV_UNDI_SHUTDOWN            undi_shutdown;
        struct s_PXENV_UNDI_OPEN                undi_open;
        struct s_PXENV_UNDI_CLOSE               undi_close;
@@ -62,16 +63,26 @@ typedef enum {
        READY
 } pxe_stack_state_t;
 
+#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \
+                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+                       return PXENV_EXIT_FAILURE; }
+#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \
+                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+                       return PXENV_EXIT_FAILURE; }
+#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \
+                       structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+                       return PXENV_EXIT_FAILURE; }
+
 /* Data structures installed as part of a PXE stack.  Architectures
  * will have extra information to append to the end of this.
  */
 #define PXE_TFTP_MAGIC_COOKIE ( ( 'P'<<24 ) | ( 'x'<<16 ) | ( 'T'<<8 ) | 'f' )
-typedef struct {
-       pxe_t           pxe     __attribute__ ((aligned(16)));
-       pxenv_t         pxenv   __attribute__ ((aligned(16)));
+typedef struct pxe_stack {
+       struct s_PXE            pxe     __attribute__ ((aligned(16)));
+       struct s_PXENV          pxenv   __attribute__ ((aligned(16)));
        pxe_stack_state_t state;
        union {
-               BOOTPLAYER      cached_info;
+               BOOTPLAYER_t    cached_info;
                char            packet[ETH_FRAME_LEN];
                struct {
                        uint32_t magic_cookie;
@@ -88,6 +99,10 @@ typedef struct {
        struct {}       arch_data __attribute__ ((aligned(16)));
 } pxe_stack_t;
 
+extern int ensure_pxe_state ( pxe_stack_state_t wanted );
+
+extern pxe_stack_t *pxe_stack;
+
 extern PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any );
 
 #endif /* PXE_H */
index 91eda5b..60d5b30 100644 (file)
@@ -200,89 +200,6 @@ typedef struct s_PXE PXE_t;
 
 /** @} */ /* pxe_api_call */
 
-/** @defgroup pxe_loader_api PXE Loader API
- *
- * The UNDI ROM loader API
- *
- * @{
- */
-
-/** The UNDI ROM ID structure */
-struct s_UNDI_ROM_ID {
-       /** Signature
-        *
-        * Contains the bytes 'U', 'N', 'D', 'I'.
-        */
-       UINT32_t Signature;
-       UINT8_t StructLength;           /**< Length of this structure */
-       /** Checksum
-        *
-        * The byte checksum of this structure (using the length in
-        * #StructLength) must be zero.
-        */
-       UINT8_t StructCksum;
-       /** Revision of this structure
-        *
-        * For PXE version 2.1, this field must be zero.
-        */
-       UINT8_t StructRev;
-       /** UNDI revision
-        *
-        * UNDI revision, least significant byte first.  For UNDI
-        * version 2.1.0, this field will contain { 0x00, 0x01, 0x02 }.
-        */
-       UINT8_t UNDIRev[3];
-       /** UNDI loader routine entry point
-        *
-        * This is the entry point for calling undi_loader().
-        */
-       UINT16_t UNDILoader;
-       /** Minimum required stack segment size */
-       UINT16_t StackSize;
-       /** Minimum required data segment size */
-       UINT16_t DataSize;
-       /** Minimum required code segment size */
-       UINT16_t CodeSize;
-} PACKED;
-
-typedef struct s_UNDI_ROM_ID UNDI_ROM_ID_t;
-
-/** Parameter block for undi_loader() */
-struct s_UNDI_LOADER {
-       /** struct s_UNDI_LOADER starts with a struct s_PXENV_START_UNDI */
-       union undi_loader_start_undi {
-               PXENV_STATUS_t Status;          /**< PXE status code */
-               /** Parameters to pass to pxenv_start_undi() */
-               struct s_PXENV_START_UNDI start_undi;
-       } u;
-       /** UNDI data segment
-        *
-        * @note The PXE specification defines the type of this field
-        * as #UINT16_t.  For x86, #SEGSEL_t and #UINT16_t are
-        * equivalent anyway; for other architectures #SEGSEL_t makes
-        * more sense.
-        */
-       SEGSEL_t        undi_ds;
-       /** UNDI code segment
-        *
-        * @note The PXE specification defines the type of this field
-        * as #UINT16_t.  For x86, #SEGSEL_t and #UINT16_t are
-        * equivalent anyway; for other architectures #SEGSEL_t makes
-        * more sense.
-        */
-       SEGSEL_t        undi_cs;
-       /** Address of the !PXE structure (a struct s_PXE) */
-       SEGOFF16_t      pxe_ptr;
-       /** Address of the PXENV+ structure (a struct s_PXENV) */
-       SEGOFF16_t      pxenv_ptr;
-} PACKED;
-
-typedef struct s_UNDI_LOADER UNDI_LOADER_t;
-
-extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
-
-/** @} */ /* pxe_loader_api */
-
 /** @defgroup pxe_preboot_api PXE Preboot API
  *
  * General high-level functions: #PXENV_UNLOAD_STACK, #PXENV_START_UNDI etc.
@@ -364,6 +281,9 @@ typedef struct s_PXENV_GET_CACHED_INFO PXENV_GET_CACHED_INFO_t;
 
 #define VM_RFC1048     0x63825363L     /**< DHCP magic cookie */
 
+/** Maximum length of DHCP options */
+#define BOOTP_DHCPVEND 1024
+
 /** Format of buffer filled in by pxenv_get_cached_info()
  *
  * This somewhat convoluted data structure simply describes the layout
@@ -986,13 +906,13 @@ struct s_PXENV_UNDI_MCAST_ADDRESS {
 typedef struct s_PXENV_UNDI_MCAST_ADDRESS PXENV_UNDI_MCAST_ADDRESS_t;
 
 /** Parameter block for pxenv_undi_reset_adapter() */
-struct s_PXENV_UNDI_RESET_ADAPTER {
+struct s_PXENV_UNDI_RESET {
        PXENV_STATUS_t  Status;         /**< PXE status code */
        /** Multicast MAC addresses */
        struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
 } PACKED;
 
-typedef struct s_PXENV_UNDI_RESET_ADAPTER PXENV_UNDI_RESET_ADAPTER_t;
+typedef struct s_PXENV_UNDI_RESET PXENV_UNDI_RESET_t;
 
 extern PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
                                               *undi_reset_adapter );
@@ -1626,6 +1546,89 @@ extern PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr );
 
 /** @} */ /* pxe_undi_api */
 
+/** @defgroup pxe_loader_api PXE Loader API
+ *
+ * The UNDI ROM loader API
+ *
+ * @{
+ */
+
+/** The UNDI ROM ID structure */
+struct s_UNDI_ROM_ID {
+       /** Signature
+        *
+        * Contains the bytes 'U', 'N', 'D', 'I'.
+        */
+       UINT32_t Signature;
+       UINT8_t StructLength;           /**< Length of this structure */
+       /** Checksum
+        *
+        * The byte checksum of this structure (using the length in
+        * #StructLength) must be zero.
+        */
+       UINT8_t StructCksum;
+       /** Revision of this structure
+        *
+        * For PXE version 2.1, this field must be zero.
+        */
+       UINT8_t StructRev;
+       /** UNDI revision
+        *
+        * UNDI revision, least significant byte first.  For UNDI
+        * version 2.1.0, this field will contain { 0x00, 0x01, 0x02 }.
+        */
+       UINT8_t UNDIRev[3];
+       /** UNDI loader routine entry point
+        *
+        * This is the entry point for calling undi_loader().
+        */
+       UINT16_t UNDILoader;
+       /** Minimum required stack segment size */
+       UINT16_t StackSize;
+       /** Minimum required data segment size */
+       UINT16_t DataSize;
+       /** Minimum required code segment size */
+       UINT16_t CodeSize;
+} PACKED;
+
+typedef struct s_UNDI_ROM_ID UNDI_ROM_ID_t;
+
+/** Parameter block for undi_loader() */
+struct s_UNDI_LOADER {
+       /** struct s_UNDI_LOADER starts with a struct s_PXENV_START_UNDI */
+       union undi_loader_start_undi {
+               PXENV_STATUS_t Status;          /**< PXE status code */
+               /** Parameters to pass to pxenv_start_undi() */
+               struct s_PXENV_START_UNDI start_undi;
+       } u;
+       /** UNDI data segment
+        *
+        * @note The PXE specification defines the type of this field
+        * as #UINT16_t.  For x86, #SEGSEL_t and #UINT16_t are
+        * equivalent anyway; for other architectures #SEGSEL_t makes
+        * more sense.
+        */
+       SEGSEL_t        undi_ds;
+       /** UNDI code segment
+        *
+        * @note The PXE specification defines the type of this field
+        * as #UINT16_t.  For x86, #SEGSEL_t and #UINT16_t are
+        * equivalent anyway; for other architectures #SEGSEL_t makes
+        * more sense.
+        */
+       SEGSEL_t        undi_cs;
+       /** Address of the !PXE structure (a struct s_PXE) */
+       SEGOFF16_t      pxe_ptr;
+       /** Address of the PXENV+ structure (a struct s_PXENV) */
+       SEGOFF16_t      pxenv_ptr;
+} PACKED;
+
+typedef struct s_UNDI_LOADER UNDI_LOADER_t;
+
+extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
+
+/** @} */ /* pxe_loader_api */
+
 /** @} */ /* pxe */
 
 #endif /* PXE_API_H */
diff --git a/src/include/pxe_export.h b/src/include/pxe_export.h
deleted file mode 100644 (file)
index 3d39e73..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Header for pxe_export.c
- */
-
-#ifndef PXE_EXPORT_H
-#define PXE_EXPORT_H
-
-#include "pxe.h"
-
-/* Function prototypes */
-extern int ensure_pxe_state ( pxe_stack_state_t wanted );
-
-extern PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI * );
-extern PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP * );
-extern PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP * );
-extern PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE * );
-extern PXENV_EXIT_t pxenv_undi_reset_adapter ( t_PXENV_UNDI_RESET_ADAPTER * );
-extern PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN * );
-extern PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN * );
-extern PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE * );
-extern PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT * );
-extern PXENV_EXIT_t pxenv_undi_set_mcast_address (
-                                           t_PXENV_UNDI_SET_MCAST_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_set_station_address (
-                                         t_PXENV_UNDI_SET_STATION_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_set_packet_filter (
-                                           t_PXENV_UNDI_SET_PACKET_FILTER * );
-extern PXENV_EXIT_t pxenv_undi_get_information (
-                                             t_PXENV_UNDI_GET_INFORMATION * );
-extern PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS* );
-extern PXENV_EXIT_t pxenv_undi_clear_statistics (
-                                            t_PXENV_UNDI_CLEAR_STATISTICS * );
-extern PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS* );
-extern PXENV_EXIT_t pxenv_undi_force_interrupt (
-                                             t_PXENV_UNDI_FORCE_INTERRUPT * );
-extern PXENV_EXIT_t pxenv_undi_get_mcast_address (
-                                           t_PXENV_UNDI_GET_MCAST_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_get_nic_type ( t_PXENV_UNDI_GET_NIC_TYPE * );
-extern PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO *);
-extern PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR * );
-extern PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI * );
-extern PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN * );
-extern PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE * );
-extern PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ * );
-extern PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE * );
-extern PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE * );
-extern PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN * );
-extern PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE * );
-extern PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ * );
-extern PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE * );
-extern PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK * );
-extern PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO * );
-extern PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP * );
-extern PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE * );
-extern PXENV_EXIT_t pxenv_stop_base ( t_PXENV_STOP_BASE * );
-
-extern PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params );
-
-/* Static variables */
-extern pxe_stack_t *pxe_stack;
-
-#endif /* PXE_EXPORT_H */
index 9b24301..80e6aa1 100644 (file)
@@ -117,7 +117,7 @@ typedef struct s_SEGDESC {
        SEGSEL_t        segment_address;        /**< Segment selector */
        ADDR32_t        physical_address;       /**< Segment base address */
        OFF16_t         seg_size;               /**< Size of the segment */
-} PACKED __SEGDESC_t;
+} PACKED SEGDESC_t;
 
 /** @} */ /* pxe_types */
 
diff --git a/src/interface/pxe/pxe.c b/src/interface/pxe/pxe.c
new file mode 100644 (file)
index 0000000..06e6121
--- /dev/null
@@ -0,0 +1,311 @@
+/** @file
+ *
+ * 
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pxe.h"
+
+/* Global pointer to currently installed PXE stack */
+pxe_stack_t *pxe_stack = NULL;
+
+/* Various startup/shutdown routines.  The startup/shutdown call
+ * sequence is incredibly badly defined in the Intel PXE spec, for
+ * example:
+ *
+ *   PXENV_UNDI_INITIALIZE says that the parameters used to initialize
+ *   the adaptor should be those supplied to the most recent
+ *   PXENV_UNDI_STARTUP call.  PXENV_UNDI_STARTUP takes no parameters.
+ *
+ *   PXENV_UNDI_CLEANUP says that the rest of the API will not be
+ *   available after making this call.  Figure 3-3 ("Early UNDI API
+ *   usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
+ *   call to the supposedly now unavailable PXENV_STOP_UNDI.
+ *
+ *   PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
+ *   occupied by the PXE stack.  Figure 4-3 ("PXE IPL") shows a call
+ *   to PXENV_STOP_UNDI being made after the call to
+ *   PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
+ *   should have been freed (and, potentially, zeroed).
+ *
+ *   Nothing, anywhere, seems to mention who's responsible for freeing
+ *   up the base memory allocated for the stack segment.  It's not
+ *   even clear whether or not this is expected to be in free base
+ *   memory rather than claimed base memory.
+ *
+ * Consequently, we adopt a rather defensive strategy, designed to
+ * work with any conceivable sequence of initialisation or shutdown
+ * calls.  We have only two things that we care about:
+ *
+ *   1. Have we hooked INT 1A and INT 15,E820(etc.)?
+ *   2. Is the NIC initialised?
+ *
+ * The NIC should never be initialised without the vectors being
+ * hooked, similarly the vectors should never be unhooked with the NIC
+ * still initialised.  We do, however, want to be able to have the
+ * vectors hooked with the NIC shutdown.  We therefore have three
+ * possible states:
+ *
+ *   1. Ready to unload: interrupts unhooked, NIC shutdown.
+ *   2. Midway: interrupts hooked, NIC shutdown.
+ *   3. Fully ready: interrupts hooked, NIC initialised.
+ *
+ * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
+ * these, and the call pxe_ensure_state() to ensure that the stack is
+ * in the specified state.  All our PXE API call implementations
+ * should use this call to ensure that the state is as required for
+ * that PXE API call.  This enables us to cope with whatever the
+ * end-user's interpretation of the PXE spec may be.  It even allows
+ * for someone calling e.g. PXENV_START_UNDI followed by
+ * PXENV_UDP_WRITE, without bothering with any of the intervening
+ * calls.
+ *
+ * pxe_ensure_state() returns 1 for success, 0 for failure.  In the
+ * event of failure (which can arise from e.g. asking for state READY
+ * when we don't know where our NIC is), the error code
+ * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
+ * The macros ENSURE_XXX() can be used to achieve this without lots of
+ * duplicated code.
+ */
+
+/* pxe_[un]hook_stack are architecture-specific and provided in
+ * pxe_callbacks.c
+ */
+
+int pxe_initialise_nic ( void ) {
+       if ( pxe_stack->state >= READY ) return 1;
+
+#warning "device probing mechanism has completely changed"
+#if 0
+
+       /* Check if NIC is initialised.  dev.disable is set to 0
+        * when disable() is called, so we use this.
+        */
+       if ( dev.disable ) {
+               /* NIC may have been initialised independently
+                * (e.g. when we set up the stack prior to calling the
+                * NBP).
+                */
+               pxe_stack->state = READY;
+               return 1;
+       }
+
+       /* If we already have a NIC defined, reuse that one with
+        * PROBE_AWAKE.  If one was specifed via PXENV_START_UNDI, try
+        * that one first.  Otherwise, set PROBE_FIRST.
+        */
+
+       if ( dev.state.pci.dev.use_specified == 1 ) {
+               dev.how_probe = PROBE_NEXT;
+               DBG ( " initialising NIC specified via START_UNDI" );
+       } else if ( dev.state.pci.dev.driver ) {
+               DBG ( " reinitialising NIC" );
+               dev.how_probe = PROBE_AWAKE;
+       } else {
+               DBG ( " probing for any NIC" );
+               dev.how_probe = PROBE_FIRST;
+       }
+
+       /* Call probe routine to bring up the NIC */
+       if ( eth_probe ( &dev ) != PROBE_WORKED ) {
+               DBG ( " failed" );
+               return 0;
+       }
+#endif
+       
+
+       pxe_stack->state = READY;
+       return 1;
+}
+
+int pxe_shutdown_nic ( void ) {
+       if ( pxe_stack->state <= MIDWAY ) return 1;
+
+       eth_irq ( DISABLE );
+       disable ( &dev );
+       pxe_stack->state = MIDWAY;
+       return 1;
+}
+
+int ensure_pxe_state ( pxe_stack_state_t wanted ) {
+       int success = 1;
+
+       if ( ! pxe_stack ) return 0;
+       if ( wanted >= MIDWAY )
+               success = success & hook_pxe_stack();
+       if ( wanted > MIDWAY ) {
+               success = success & pxe_initialise_nic();
+       } else {
+               success = success & pxe_shutdown_nic();
+       }
+       if ( wanted < MIDWAY )
+               success = success & unhook_pxe_stack();
+       return success;
+}
+
+/* API call dispatcher
+ *
+ * Status: complete
+ */
+PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any ) {
+       PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
+
+       /* Set default status in case child routine fails to do so */
+       any->Status = PXENV_STATUS_FAILURE;
+
+       DBG ( "[" );
+
+       /* Hand off to relevant API routine */
+       switch ( opcode ) {
+       case PXENV_START_UNDI:
+               ret = pxenv_start_undi ( &any->start_undi );
+               break;
+       case PXENV_UNDI_STARTUP:
+               ret = pxenv_undi_startup ( &any->undi_startup );
+               break;
+       case PXENV_UNDI_CLEANUP:
+               ret = pxenv_undi_cleanup ( &any->undi_cleanup );
+               break;
+       case PXENV_UNDI_INITIALIZE:
+               ret = pxenv_undi_initialize ( &any->undi_initialize );
+               break;
+       case PXENV_UNDI_RESET_ADAPTER:
+               ret = pxenv_undi_reset_adapter ( &any->undi_reset_adapter );
+               break;
+       case PXENV_UNDI_SHUTDOWN:
+               ret = pxenv_undi_shutdown ( &any->undi_shutdown );
+               break;
+       case PXENV_UNDI_OPEN:
+               ret = pxenv_undi_open ( &any->undi_open );
+               break;
+       case PXENV_UNDI_CLOSE:
+               ret = pxenv_undi_close ( &any->undi_close );
+               break;
+       case PXENV_UNDI_TRANSMIT:
+               ret = pxenv_undi_transmit ( &any->undi_transmit );
+               break;
+       case PXENV_UNDI_SET_MCAST_ADDRESS:
+               ret = pxenv_undi_set_mcast_address (
+                                               &any->undi_set_mcast_address );
+               break;
+       case PXENV_UNDI_SET_STATION_ADDRESS:
+               ret = pxenv_undi_set_station_address (
+                                             &any->undi_set_station_address );
+               break;
+       case PXENV_UNDI_SET_PACKET_FILTER:
+               ret = pxenv_undi_set_packet_filter (
+                                               &any->undi_set_packet_filter );
+               break;
+       case PXENV_UNDI_GET_INFORMATION:
+               ret = pxenv_undi_get_information (
+                                              &any->undi_get_information );
+               break;
+       case PXENV_UNDI_GET_STATISTICS:
+               ret = pxenv_undi_get_statistics ( &any->undi_get_statistics );
+               break;
+       case PXENV_UNDI_CLEAR_STATISTICS:
+               ret = pxenv_undi_clear_statistics (
+                                                &any->undi_clear_statistics );
+               break;
+       case PXENV_UNDI_INITIATE_DIAGS:
+               ret = pxenv_undi_initiate_diags ( &any->undi_initiate_diags );
+                                                
+               break;
+       case PXENV_UNDI_FORCE_INTERRUPT:
+               ret = pxenv_undi_force_interrupt (
+                                              &any->undi_force_interrupt );
+               break;
+       case PXENV_UNDI_GET_MCAST_ADDRESS:
+               ret = pxenv_undi_get_mcast_address (
+                                            &any->undi_get_mcast_address );
+               break;
+       case PXENV_UNDI_GET_NIC_TYPE:
+               ret = pxenv_undi_get_nic_type ( &any->undi_get_nic_type );
+               break;
+       case PXENV_UNDI_GET_IFACE_INFO:
+               ret = pxenv_undi_get_iface_info ( &any->undi_get_iface_info );
+               break;
+       case PXENV_UNDI_ISR:
+               ret = pxenv_undi_isr ( &any->undi_isr );
+               break;
+       case PXENV_STOP_UNDI:
+               ret = pxenv_stop_undi ( &any->stop_undi );
+               break;
+       case PXENV_TFTP_OPEN:
+               ret = pxenv_tftp_open ( &any->tftp_open );
+               break;
+       case PXENV_TFTP_CLOSE:
+               ret = pxenv_tftp_close ( &any->tftp_close );
+               break;
+       case PXENV_TFTP_READ:
+               ret = pxenv_tftp_read ( &any->tftp_read );
+               break;
+       case PXENV_TFTP_READ_FILE:
+               ret = pxenv_tftp_read_file ( &any->tftp_read_file );
+               break;
+       case PXENV_TFTP_GET_FSIZE:
+               ret = pxenv_tftp_get_fsize ( &any->tftp_get_fsize );
+               break;
+       case PXENV_UDP_OPEN:
+               ret = pxenv_udp_open ( &any->udp_open );
+               break;
+       case PXENV_UDP_CLOSE:
+               ret = pxenv_udp_close ( &any->udp_close );
+               break;
+       case PXENV_UDP_READ:
+               ret = pxenv_udp_read ( &any->udp_read );
+               break;
+       case PXENV_UDP_WRITE:
+               ret = pxenv_udp_write ( &any->udp_write );
+               break;
+       case PXENV_UNLOAD_STACK:
+               ret = pxenv_unload_stack ( &any->unload_stack );
+               break;
+       case PXENV_GET_CACHED_INFO:
+               ret = pxenv_get_cached_info ( &any->get_cached_info );
+               break;
+       case PXENV_RESTART_TFTP:
+               ret = pxenv_restart_tftp ( &any->restart_tftp );
+               break;
+       case PXENV_START_BASE:
+               ret = pxenv_start_base ( &any->start_base );
+               break;
+       case PXENV_STOP_BASE:
+               ret = pxenv_stop_base ( &any->stop_base );
+               break;
+               
+       default:
+               DBG ( "PXENV_UNKNOWN_%hx", opcode );
+               any->Status = PXENV_STATUS_UNSUPPORTED;
+               ret = PXENV_EXIT_FAILURE;
+               break;
+       }
+
+       if ( any->Status != PXENV_STATUS_SUCCESS ) {
+               DBG ( " %hx", any->Status );
+       }
+       if ( ret != PXENV_EXIT_SUCCESS ) {
+               DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
+       }
+       DBG ( "]" );
+
+       return ret;
+}
diff --git a/src/interface/pxe/pxe_loader.c b/src/interface/pxe/pxe_loader.c
new file mode 100644 (file)
index 0000000..2539aae
--- /dev/null
@@ -0,0 +1,83 @@
+/** @file
+ *
+ * PXE UNDI loader
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pxe.h"
+
+/* PXENV_UNDI_LOADER
+ *
+ * Status: working
+ *
+ * NOTE: This is not a genuine PXE API call; the loader has a separate
+ * entry point.  However, to simplify the mapping of the PXE API to
+ * the internal Etherboot API, both are directed through the same
+ * interface.
+ */
+PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
+       uint32_t loader_phys = virt_to_phys ( undi_loader );
+
+       DBG ( "PXENV_UNDI_LOADER" );
+       
+       /* Set UNDI DS as our real-mode stack */
+       use_undi_ds_for_rm_stack ( undi_loader->undi_ds );
+
+       /* FIXME: These lines are borrowed from main.c.  There should
+        * probably be a single initialise() function that does all
+        * this, but it's currently split interestingly between main()
+        * and main_loop()...
+        */
+
+
+       /* CHECKME: Our init functions have probably already been
+          called by the ROM prefix's call to setup(), haven't
+          they? */
+
+
+
+       /* We have relocated; the loader pointer is now invalid */
+       undi_loader = phys_to_virt ( loader_phys );
+
+       /* Install PXE stack to area specified by NBP */
+       install_pxe_stack ( VIRTUAL ( undi_loader->undi_cs, 0 ) );
+       
+       /* Call pxenv_start_undi to set parameters.  Why the hell PXE
+        * requires these parameters to be provided twice is beyond
+        * the wit of any sane man.  Don't worry if it fails; the NBP
+        * should call PXENV_START_UNDI separately anyway.
+        */
+       pxenv_start_undi ( &undi_loader->u.start_undi );
+       /* Unhook stack; the loader is not meant to hook int 1a etc,
+        * but the call the pxenv_start_undi will cause it to happen.
+        */
+
+       /* FIXME: can't use ENSURE_CAN_UNLOAD() thanks to newer gcc's
+        * barfing on unnamed struct/unions. */
+       /*      ENSURE_CAN_UNLOAD ( undi_loader ); */
+
+       /* Fill in addresses of !PXE and PXENV+ structures */
+       PTR_TO_SEGOFF16 ( &pxe_stack->pxe, undi_loader->pxe_ptr );
+       PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, undi_loader->pxenv_ptr );
+       
+       undi_loader->u.Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
diff --git a/src/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c
new file mode 100644 (file)
index 0000000..9196fe3
--- /dev/null
@@ -0,0 +1,249 @@
+/** @file
+ *
+ * PXE Preboot API
+ *
+ */
+
+/* PXE API interface for Etherboot.
+ *
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pxe.h"
+#include "pxe_callbacks.h"
+
+/**
+ * UNLOAD BASE CODE STACK
+ *
+ * @v None
+ * @ret ...
+ *
+ */
+PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
+       int success;
+
+       DBG ( "PXENV_UNLOAD_STACK" );
+       success = ensure_pxe_state ( CAN_UNLOAD );
+
+       /* We need to call cleanup() at some point.  The network card
+        * has already been disabled by ENSURE_CAN_UNLOAD(), but for
+        * the sake of completeness we should call the console_fini()
+        * etc. that are part of cleanup().
+        *
+        * There seems to be a lack of consensus on which is the final
+        * PXE API call to make, but it's a fairly safe bet that all
+        * the potential shutdown sequences will include a call to
+        * PXENV_UNLOAD_STACK at some point, so we may as well do it
+        * here.
+        */
+       cleanup();
+
+       if ( ! success ) {
+               unload_stack->Status = PXENV_STATUS_KEEP_ALL;
+               return PXENV_EXIT_FAILURE;
+       }
+
+       unload_stack->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_GET_CACHED_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
+                                    *get_cached_info ) {
+       BOOTPLAYER_t *cached_info = &pxe_stack->cached_info;
+       DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+       ENSURE_READY ( get_cached_info );
+
+       /* Fill in cached_info structure in our pxe_stack */
+
+       /* I don't think there's actually any way we can be called in
+        * the middle of a DHCP request... 
+        */
+       cached_info->opcode = BOOTP_REP;
+       /* We only have Ethernet drivers */
+       cached_info->Hardware = ETHER_TYPE;
+       cached_info->Hardlen = ETH_ALEN;
+       /* PXESPEC: "Client sets" says the spec, but who's filling in
+        * this structure?  It ain't the client.
+        */
+       cached_info->Gatehops = 0;
+       cached_info->ident = 0;
+       cached_info->seconds = 0;
+       cached_info->Flags = BOOTP_BCAST;
+       /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
+       cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
+       cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
+       cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
+       /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
+       cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+       memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
+       /* Nullify server name */
+       cached_info->Sname[0] = '\0';
+       memcpy ( cached_info->bootfile, KERNEL_BUF,
+                sizeof(cached_info->bootfile) );
+       /* Copy DHCP vendor options */
+       memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+                sizeof(cached_info->vendor.d) );
+       
+       /* Copy to user-specified buffer, or set pointer to our buffer */
+       get_cached_info->BufferLimit = sizeof(*cached_info);
+       /* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
+        * 0, but what are we supposed to do with a null buffer of
+        * non-zero size?!
+        */
+       if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
+               /* Point back to our buffer */
+               PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
+               get_cached_info->BufferSize = sizeof(*cached_info);
+       } else {
+               /* Copy to user buffer */
+               size_t size = sizeof(*cached_info);
+               void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
+               if ( get_cached_info->BufferSize < size )
+                       size = get_cached_info->BufferSize;
+               DBG ( " to %x", virt_to_phys ( buffer ) );
+               memcpy ( buffer, cached_info, size );
+               /* PXESPEC: Should we return an error if the user
+                * buffer is too small?  We do return the actual size
+                * of the buffer via BufferLimit, so the user does
+                * have a way to detect this already.
+                */
+       }
+
+       get_cached_info->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_RESTART_TFTP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
+                                 *restart_tftp ) {
+       PXENV_EXIT_t tftp_exit;
+
+       DBG ( "PXENV_RESTART_TFTP" );
+       ENSURE_READY ( restart_tftp );
+
+       /* Words cannot describe the complete mismatch between the PXE
+        * specification and any possible version of reality...
+        */
+       restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
+       restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
+       DBG ( "(" );
+       tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (union u_PXENV_ANY*)restart_tftp );
+       DBG ( ")" );
+       if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
+
+       /* Fire up the new NBP */
+       restart_tftp->Status = xstartpxe();
+
+       /* Not sure what "SUCCESS" actually means, since we can only
+        * return if the new NBP failed to boot...
+        */
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
+       unsigned char bus, devfn;
+
+       DBG ( "PXENV_START_UNDI" );
+       ENSURE_MIDWAY(start_undi);
+
+       /* Record PCI bus & devfn passed by caller, so we know which
+        * NIC they want to use.
+        *
+        * If they don't match our already-existing NIC structure, set
+        * values to ensure that the specified NIC is used at the next
+        * call to pxe_intialise_nic().
+        */
+       bus = ( start_undi->AX >> 8 ) & 0xff;
+       devfn = start_undi->AX & 0xff;
+
+#warning "device probing mechanism has completely changed"
+#if 0
+       if ( ( pci->dev.driver == NULL ) ||
+            ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
+               /* This is quite a bit of a hack and relies on
+                * knowledge of the internal operation of Etherboot's
+                * probe mechanism.
+                */
+               DBG ( " set PCI %hhx:%hhx.%hhx",
+                     bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
+               dev->type = BOOT_NIC;
+               dev->to_probe = PROBE_PCI;
+               memset ( &dev->state, 0, sizeof(dev->state) );
+               pci->advance = 1;
+               pci->dev.use_specified = 1;
+               pci->dev.bus = bus;
+               pci->dev.devfn = devfn;
+       }
+#endif
+
+       start_undi->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_STOP_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
+       DBG ( "PXENV_STOP_UNDI" );
+
+       if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
+               stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
+               return PXENV_EXIT_FAILURE;
+       }
+
+       stop_undi->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_BASE
+ *
+ * Status: won't implement (requires major structural changes)
+ */
+PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
+       DBG ( "PXENV_START_BASE" );
+       /* ENSURE_READY ( start_base ); */
+       start_base->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_STOP_BASE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
+       DBG ( "PXENV_STOP_BASE" );
+
+       /* The only time we will be called is when the NBP is trying
+        * to shut down the PXE stack.  There's nothing we need to do
+        * in this call.
+        */
+
+       stop_base->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
diff --git a/src/interface/pxe/pxe_tftp.c b/src/interface/pxe/pxe_tftp.c
new file mode 100644 (file)
index 0000000..bc4b650
--- /dev/null
@@ -0,0 +1,199 @@
+/** @file
+ *
+ * PXE TFTP API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pxe.h"
+
+/* PXENV_TFTP_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
+       struct sockaddr_in tftp_server;
+       struct tftpreq_info_t request;
+       struct tftpblk_info_t block;
+
+       DBG ( "PXENV_TFTP_OPEN" );
+       ENSURE_READY ( tftp_open );
+
+       /* Set server address and port */
+       tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
+               ? tftp_open->ServerIPAddress
+               : arptable[ARP_SERVER].ipaddr.s_addr;
+       tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
+#ifdef WORK_AROUND_BPBATCH_BUG        
+       /* Force use of port 69; BpBatch tries to use port 4 for some         
+       * bizarre reason.         */        
+       tftp_server.sin_port = TFTP_PORT;
+#endif
+       /* Ignore gateway address; we can route properly */
+       /* Fill in request structure */
+       request.server = &tftp_server;
+       request.name = tftp_open->FileName;
+       request.blksize = tftp_open->PacketSize;
+       DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
+             tftp_open->TFTPPort, request.name, request.blksize );
+       if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
+       /* Make request and get first packet */
+       if ( !tftp_block ( &request, &block ) ) {
+               tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+               return PXENV_EXIT_FAILURE;
+       }
+       /* Fill in PacketSize */
+       tftp_open->PacketSize = request.blksize;
+       /* Store first block for later retrieval by TFTP_READ */
+       pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
+       pxe_stack->tftpdata.len = block.len;
+       pxe_stack->tftpdata.eof = block.eof;
+       memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
+
+       tftp_open->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
+       DBG ( "PXENV_TFTP_CLOSE" );
+       ENSURE_READY ( tftp_close );
+       tftp_close->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_READ
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
+       struct tftpblk_info_t block;
+
+       DBG ( "PXENV_TFTP_READ" );
+       ENSURE_READY ( tftp_read );
+
+       /* Do we have a block pending */
+       if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
+               block.data = pxe_stack->tftpdata.data;
+               block.len = pxe_stack->tftpdata.len;
+               block.eof = pxe_stack->tftpdata.eof;
+               block.block = 1; /* Will be the first block */
+               pxe_stack->tftpdata.magic_cookie = 0;
+       } else {
+               if ( !tftp_block ( NULL, &block ) ) {
+                       tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+                       return PXENV_EXIT_FAILURE;
+               }
+       }
+
+       /* Return data */
+       tftp_read->PacketNumber = block.block;
+       tftp_read->BufferSize = block.len;
+       memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
+       DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
+             tftp_read->Buffer.offset );
+       tftp_read->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_READ_FILE
+ *
+ * Status: working
+ */
+
+int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused,
+                         unsigned int len, int eof ) {
+       if ( pxe_stack->readfile.buffer ) {
+               if ( pxe_stack->readfile.offset + len >=
+                    pxe_stack->readfile.bufferlen ) return -1;
+               memcpy ( pxe_stack->readfile.buffer +
+                        pxe_stack->readfile.offset, data, len );
+       }
+       pxe_stack->readfile.offset += len;
+       return eof ? 0 : 1;
+}
+
+PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+                                   *tftp_read_file ) {
+       struct sockaddr_in tftp_server;
+       int rc;
+
+       DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
+             tftp_read_file->Buffer,
+             tftp_read_file->Buffer + tftp_read_file->BufferSize );
+       ENSURE_READY ( tftp_read_file );
+
+       /* inserted by Klaus Wittemeier */
+       /* KERNEL_BUF stores the name of the last required file */
+       /* This is a fix to make Microsoft Remote Install Services work (RIS) */
+       memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
+       /* end of insertion */
+
+       /* Set server address and port */
+       tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
+               ? tftp_read_file->ServerIPAddress
+               : arptable[ARP_SERVER].ipaddr.s_addr;
+       tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
+
+       pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
+       pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
+       pxe_stack->readfile.offset = 0;
+
+       rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
+                   pxe_tftp_read_block );
+       if ( rc ) {
+               tftp_read_file->Status = PXENV_STATUS_FAILURE;
+               return PXENV_EXIT_FAILURE;
+       }
+       tftp_read_file->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_GET_FSIZE
+ *
+ * Status: working, though ugly (we actually read the whole file,
+ * because it's too ugly to make Etherboot request the tsize option
+ * and hand it to us).
+ */
+PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+                                   *tftp_get_fsize ) {
+       int rc;
+
+       DBG ( "PXENV_TFTP_GET_FSIZE" );
+       ENSURE_READY ( tftp_get_fsize );
+
+       pxe_stack->readfile.buffer = NULL;
+       pxe_stack->readfile.bufferlen = 0;
+       pxe_stack->readfile.offset = 0;
+
+#warning "Rewrite pxenv_tftp_get_fsize, please"
+       if ( rc ) {
+               tftp_get_fsize->FileSize = 0;
+               tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
+               return PXENV_EXIT_FAILURE;
+       }
+       tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
+       tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
index bf0a19f..e818fc7 100644 (file)
@@ -5,6 +5,8 @@
  */
 
 #include "pxe.h"
+#include "io.h"
+#include "string.h"
 
 /*
  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
  */
 
 /**
- * UDP OPEN (#PXENV_UDP_OPEN)
+ * UDP OPEN
  *
  * @v udp_open                         Pointer to a struct s_PXENV_UDP_OPEN
  * @v s_PXENV_UDP_OPEN::src_ip         IP address of this station, or 0.0.0.0
- * @ret PXENV_EXIT_SUCCESS             Always
+ * @ret #PXENV_EXIT_SUCCESS            Always
  * @ret s_PXENV_UDP_OPEN::Status       PXE status code
- * @err PXENV_STATUS_UNDI_INVALID_STATE        NIC could not be initialised
+ * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
  *
  * Prepares the PXE stack for communication using pxenv_udp_write()
  * and pxenv_udp_read().  The IP address supplied in
  *   - you take the multiple connections into account when calling
  *     pxenv_udp_read().
  *
- * You can call pxenv_udp_open() in real mode, 16-bit protected mode
- * with a 16-bit stack segment, 16-bit protected mode with a 32-bit
- * stack segment, or V86 mode.  The pxe::StatusCallout field may be
- * zero even in protected mode.
+ * On x86, you can call pxenv_udp_open() in real mode, 16-bit
+ * protected mode with a 16-bit stack segment, 16-bit protected mode
+ * with a 32-bit stack segment, or V86 mode.  The pxe::StatusCallout
+ * field may be zero even in protected mode.
  * 
+ * @note The PXE specification states that you have only one UDP
+ * connection open at a time, and that you cannot have a UDP
+ * connection open simultaneously with a TFTP connection.  Etherboot
+ * does not enforce this unnecessary restriction.
+ *
  */
 PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
        DBG ( "PXENV_UDP_OPEN" );
@@ -71,10 +78,10 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
 }
 
 /**
- * UDP CLOSE (#PXENV_UDP_CLOSE)
+ * UDP CLOSE
  *
  * @v udp_close                                Pointer to a struct s_PXENV_UDP_CLOSE
- * @ret PXENV_EXIT_SUCCESS             Always
+ * @ret #PXENV_EXIT_SUCCESS            Always
  * @ret s_PXENV_UDP_CLOSE::Status      PXE status code
  * @err None
  *
@@ -89,6 +96,11 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
  * stack segment, or V86 mode.  The pxe::StatusCallout field may be
  * zero even in protected mode.
  *
+ * @note The PXE specification states that you have only one UDP
+ * connection open at a time, and that you cannot have a UDP
+ * connection open simultaneously with a TFTP connection.  Etherboot
+ * does not enforce this unnecessary restriction.
+ *
  */
 PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
        DBG ( "PXENV_UDP_CLOSE" );
@@ -97,7 +109,7 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
 }
 
 /**
- * UDP WRITE (#PXENV_UDP_WRITE)
+ * UDP WRITE
  *
  * @v udp_write                                Pointer to a struct s_PXENV_UDP_WRITE
  * @v s_PXENV_UDP_WRITE::ip            Destination IP address
@@ -106,11 +118,11 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
  * @v s_PXENV_UDP_WRITE::dst_port      Destination UDP port
  * @v s_PXENV_UDP_WRITE::buffer_size   Length of the UDP payload
  * @v s_PXENV_UDP_WRITE::buffer                Address of the UDP payload
- * @ret PXENV_EXIT_SUCCESS             Packet was transmitted successfully
- * @ret PXENV_EXIT_FAILURE             Packet could not be transmitter
+ * @ret #PXENV_EXIT_SUCCESS            Packet was transmitted successfully
+ * @ret #PXENV_EXIT_FAILURE            Packet could not be transmitter
  * @ret s_PXENV_UDP_WRITE::Status      PXE status code
- * @err PXENV_STATUS_UNDI_INVALID_STATE        NIC could not be initialised
- * @err PXENV_STATUS_OUT_OF_RESOURCES  Packet was too large to transmit
+ * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Packet was too large to transmit
  * @err other                          Any error from pxenv_undi_transmit()
  *
  * Transmits a single UDP packet.  A valid IP and UDP header will be
@@ -136,6 +148,11 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
  * stack segment, or V86 mode.  The pxe::StatusCallout field may be
  * zero even in protected mode.
  *
+ * @note The PXE specification states that you have only one UDP
+ * connection open at a time, and that you cannot have a UDP
+ * connection open simultaneously with a TFTP connection.  Etherboot
+ * does not enforce this unnecessary restriction.
+ *
  * @bug s_PXENV_UDP_WRITE::gw is ignored; the default routing table is
  * always used.
  *
@@ -187,7 +204,7 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
                           unsigned short ptype __unused,
                           struct iphdr *ip, struct udphdr *udp,
                           struct tcphdr *tcp __unused ) {
-       t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr;
+       struct s_PXENV_UDP_READ *udp_read = (struct s_PXENV_UDP_READ*)ptr;
        uint16_t d_port;
        size_t size;
 
@@ -237,24 +254,24 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
 }
 
 /**
- * UDP READ (#PXENV_UDP_READ)
+ * UDP READ
  *
  * @v udp_read                         Pointer to a struct s_PXENV_UDP_READ
  * @v s_PXENV_UDP_READ::dest_ip                Destination IP address, or 0.0.0.0
  * @v s_PXENV_UDP_READ::d_port         Destination UDP port, or 0
  * @v s_PXENV_UDP_READ::buffer_size    Size of the UDP payload buffer
  * @v s_PXENV_UDP_READ::buffer         Address of the UDP payload buffer
- * @ret PXENV_EXIT_SUCCESS             A packet has been received
- * @ret PXENV_EXIT_FAILURE             No packet has been received
+ * @ret #PXENV_EXIT_SUCCESS            A packet has been received
+ * @ret #PXENV_EXIT_FAILURE            No packet has been received
  * @ret s_PXENV_UDP_READ::Status       PXE status code
  * @ret s_PXENV_UDP_READ::src_ip       Source IP address
- * @ret s_PXEND_UDP_READ::dest_ip      Destination IP address
+ * @ret s_PXENV_UDP_READ::dest_ip      Destination IP address
  * @ret s_PXENV_UDP_READ::s_port       Source UDP port
  * @ret s_PXENV_UDP_READ::d_port       Destination UDP port
  * @ret s_PXENV_UDP_READ::buffer_size  Length of UDP payload
- * @err PXENV_STATUS_UNDI_INVALID_STATE        NIC could not be initialised
- * @err PXENV_STATUS_OUT_OF_RESOURCES  Buffer was too small for payload
- * @err PXENV_STATUS_FAILURE           No packet was ready to read
+ * @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Buffer was too small for payload
+ * @err #PXENV_STATUS_FAILURE          No packet was ready to read
  *
  * Receive a single UDP packet.  This is a non-blocking call; if no
  * packet is ready to read, the call will return instantly with
@@ -277,6 +294,11 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
  * stack segment, or V86 mode.  The pxe::StatusCallout field may be
  * zero even in protected mode.
  *
+ * @note The PXE specification states that you have only one UDP
+ * connection open at a time, and that you cannot have a UDP
+ * connection open simultaneously with a TFTP connection.  Etherboot
+ * does not enforce this unnecessary restriction.
+ *
  * @note The PXE specification (version 2.1) does not state that we
  * should fill in s_PXENV_UDP_READ::dest_ip and
  * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c
new file mode 100644 (file)
index 0000000..3919915
--- /dev/null
@@ -0,0 +1,538 @@
+/** @file
+ *
+ * PXE UNDI API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pxe.h"
+
+typedef struct {
+       char dest[ETH_ALEN];
+       char source[ETH_ALEN];
+       uint16_t nstype;
+} media_header_t;
+
+static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
+       DBG ( "PXENV_UNDI_STARTUP" );
+       ENSURE_MIDWAY(undi_startup);
+
+       undi_startup->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEANUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
+       DBG ( "PXENV_UNDI_CLEANUP" );
+       ENSURE_CAN_UNLOAD ( undi_cleanup );
+
+       undi_cleanup->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIALIZE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
+                                    *undi_initialize ) {
+       DBG ( "PXENV_UNDI_INITIALIZE" );
+       ENSURE_MIDWAY ( undi_initialize );
+
+       undi_initialize->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_RESET_ADAPTER
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
+                                       *undi_reset_adapter ) {
+       DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+
+       ENSURE_MIDWAY ( undi_reset_adapter );
+       ENSURE_READY ( undi_reset_adapter );
+
+       undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SHUTDOWN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
+                                  *undi_shutdown ) {
+       DBG ( "PXENV_UNDI_SHUTDOWN" );
+       ENSURE_MIDWAY ( undi_shutdown );
+
+       undi_shutdown->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
+       DBG ( "PXENV_UNDI_OPEN" );
+       ENSURE_READY ( undi_open );
+
+       /* PXESPEC: This is where we choose to enable interrupts.
+        * Can't actually find where we're meant to in the PXE spec,
+        * but this should work.
+        */
+       eth_irq ( ENABLE );
+
+       undi_open->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
+       DBG ( "PXENV_UNDI_CLOSE" );
+       ENSURE_MIDWAY ( undi_close );
+
+       undi_close->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_TRANSMIT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
+                                  *undi_transmit ) {
+       struct s_PXENV_UNDI_TBD *tbd;
+       const char *dest;
+       unsigned int type;
+       unsigned int length;
+       const char *data;
+       media_header_t *media_header;
+
+       DBG ( "PXENV_UNDI_TRANSMIT" );
+       ENSURE_READY ( undi_transmit );
+
+       /* We support only the "immediate" portion of the TBD.  Who
+        * knows what Intel's "engineers" were smoking when they came
+        * up with the array of transmit data blocks...
+        */
+       tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
+       if ( tbd->DataBlkCount > 0 ) {
+               undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+               return PXENV_EXIT_FAILURE;
+       }
+       data = SEGOFF16_TO_PTR ( tbd->Xmit );
+       length = tbd->ImmedLength;
+
+       /* If destination is broadcast, we need to supply the MAC address */
+       if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
+               dest = broadcast_mac;
+       } else {
+               dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
+       }
+
+       /* We can't properly support P_UNKNOWN without rewriting all
+        * the driver transmit() methods, so we cheat: if P_UNKNOWN is
+        * specified we rip the destination address and type out of
+        * the pre-assembled packet, then skip over the header.
+        */
+       switch ( undi_transmit->Protocol ) {
+       case P_IP:      type = IP;      break;
+       case P_ARP:     type = ARP;     break;
+       case P_RARP:    type = RARP;    break;
+       case P_UNKNOWN:
+               media_header = (media_header_t*)data;
+               dest = media_header->dest;
+               type = ntohs ( media_header->nstype );
+               data += ETH_HLEN;
+               length -= ETH_HLEN;
+               break;
+       default:
+               undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+               return PXENV_EXIT_FAILURE;
+       }
+
+       /* Send the packet */
+       eth_transmit ( dest, type, length, data );
+       
+       undi_transmit->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t
+pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
+                              *undi_set_mcast_address ) {
+       DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
+       /* ENSURE_READY ( undi_set_mcast_address ); */
+       undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working (deliberately incomplete)
+ */
+PXENV_EXIT_t 
+pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
+                                *undi_set_station_address ) {
+       DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
+       ENSURE_READY ( undi_set_station_address );
+
+       /* We don't offer a facility to set the MAC address; this
+        * would require adding extra code to all the Etherboot
+        * drivers, for very little benefit.  If we're setting it to
+        * the current value anyway then return success, otherwise
+        * return UNSUPPORTED.
+        */
+       if ( memcmp ( nic.node_addr,
+                     &undi_set_station_address->StationAddress,
+                     ETH_ALEN ) == 0 ) {
+               undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+               return PXENV_EXIT_SUCCESS;
+       }
+       undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t
+pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
+                              *undi_set_packet_filter ) {
+       DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
+       /* ENSURE_READY ( undi_set_packet_filter ); */
+       undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_INFORMATION
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
+                                         *undi_get_information ) {
+       DBG ( "PXENV_UNDI_GET_INFORMATION" );
+       ENSURE_READY ( undi_get_information );
+
+       undi_get_information->BaseIo = nic.ioaddr;
+       undi_get_information->IntNumber = nic.irqno;
+       /* Cheat: assume all cards can cope with this */
+       undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+       /* Cheat: we only ever have Ethernet cards */
+       undi_get_information->HwType = ETHER_TYPE;
+       undi_get_information->HwAddrLen = ETH_ALEN;
+       /* Cheat: assume card is always configured with its permanent
+        * node address.  This is a valid assumption within Etherboot
+        * at the time of writing.
+        */
+       memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
+                ETH_ALEN );
+       memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
+                ETH_ALEN );
+       undi_get_information->ROMAddress = 0;
+               /* nic.rom_info->rom_segment; */
+       /* We only provide the ability to receive or transmit a single
+        * packet at a time.  This is a bootloader, not an OS.
+        */
+       undi_get_information->RxBufCt = 1;
+       undi_get_information->TxBufCt = 1;
+       undi_get_information->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATISTICS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
+                                        *undi_get_statistics ) {
+       DBG ( "PXENV_UNDI_GET_STATISTICS" );
+       /* ENSURE_READY ( undi_get_statistics ); */
+       undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
+                                          *undi_clear_statistics ) {
+       DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
+       /* ENSURE_READY ( undi_clear_statistics ); */
+       undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_INITIATE_DIAGS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
+                                        *undi_initiate_diags ) {
+       DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
+       /* ENSURE_READY ( undi_initiate_diags ); */
+       undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
+                                         *undi_force_interrupt ) {
+       DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
+       ENSURE_READY ( undi_force_interrupt );
+
+       eth_irq ( FORCE );
+       undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t
+pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
+                              *undi_get_mcast_address ) {
+       DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
+       /* ENSURE_READY ( undi_get_mcast_address ); */
+       undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_NIC_TYPE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
+                                      *undi_get_nic_type ) {
+#warning "device probing mechanism has changed completely"
+
+#if 0
+       struct dev *dev = &dev;
+       
+       DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
+       ENSURE_READY ( undi_get_nic_type );
+       
+       if ( dev->to_probe == PROBE_PCI ) {
+               struct pci_device *pci = &dev->state.pci.dev;
+
+               undi_get_nic_type->NicType = PCI_NIC;
+               undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
+               undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
+               undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
+               undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
+               undi_get_nic_type->info.pci.BusDevFunc =
+                       ( pci->bus << 8 ) | pci->devfn;
+               /* Cheat: these fields are probably unnecessary, and
+                * would require adding extra code to pci.c.
+                */
+               undi_get_nic_type->info.pci.Prog_Intf = 0;
+               undi_get_nic_type->info.pci.Rev = 0;
+               undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
+               undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+       } else if ( dev->to_probe == PROBE_ISA ) {
+               /* const struct isa_driver *isa = dev->state.isa.driver; */
+
+               undi_get_nic_type->NicType = PnP_NIC;
+               /* Don't think anything fills these fields in, and
+                * probably no-one will ever be interested in them.
+                */
+               undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
+               undi_get_nic_type->info.pnp.Base_Class = 0;
+               undi_get_nic_type->info.pnp.Sub_Class = 0;
+               undi_get_nic_type->info.pnp.Prog_Intf = 0;
+               undi_get_nic_type->info.pnp.CardSelNum = 0;
+       } else {
+               /* PXESPEC: There doesn't seem to be an "unknown type"
+                * defined.
+                */
+               undi_get_nic_type->NicType = 0;
+       }
+       undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+
+#endif
+}
+
+/* PXENV_UNDI_GET_IFACE_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
+                                        *undi_get_iface_info ) {
+       DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
+       ENSURE_READY ( undi_get_iface_info );
+
+       /* Just hand back some info, doesn't really matter what it is.
+        * Most PXE stacks seem to take this approach.
+        */
+       sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
+       undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
+       undi_get_iface_info->ServiceFlags = 0;
+       memset ( undi_get_iface_info->Reserved, 0,
+                sizeof(undi_get_iface_info->Reserved) );
+       undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATE
+ *
+ * Status: impossible
+ */
+PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
+                                   *undi_get_state ) {
+       undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
+       return PXENV_EXIT_FAILURE;
+};
+
+/* PXENV_UNDI_ISR
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
+       media_header_t *media_header = (media_header_t*)nic.packet;
+
+       DBG ( "PXENV_UNDI_ISR" );
+       /* We can't call ENSURE_READY, because this could be being
+        * called as part of an interrupt service routine.  Instead,
+        * we should simply die if we're not READY.
+        */
+       if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
+               undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+               return PXENV_EXIT_FAILURE;
+       }
+       
+       /* Just in case some idiot actually looks at these fields when
+        * we weren't meant to fill them in...
+        */
+       undi_isr->BufferLength = 0;
+       undi_isr->FrameLength = 0;
+       undi_isr->FrameHeaderLength = 0;
+       undi_isr->ProtType = 0;
+       undi_isr->PktType = 0;
+
+       switch ( undi_isr->FuncFlag ) {
+       case PXENV_UNDI_ISR_IN_START :
+               /* Is there a packet waiting?  If so, disable
+                * interrupts on the NIC and return "it's ours".  Do
+                * *not* necessarily acknowledge the interrupt; this
+                * can happen later when eth_poll(1) is called.  As
+                * long as the interrupt is masked off so that it
+                * doesn't immediately retrigger the 8259A then all
+                * should be well.
+                */
+               DBG ( " START" );
+               if ( eth_poll ( 0 ) ) {
+                       DBG ( " OURS" );
+                       eth_irq ( DISABLE );
+                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+               } else {
+                       DBG ( " NOT_OURS" );
+                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
+               }
+               break;
+       case PXENV_UNDI_ISR_IN_PROCESS :
+               /* Call poll(), return packet.  If no packet, return "done".
+                */
+               DBG ( " PROCESS" );
+               if ( eth_poll ( 1 ) ) {
+                       DBG ( " RECEIVE %d", nic.packetlen );
+                       if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
+                               /* Should never happen */
+                               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+                               undi_isr->Status =
+                                       PXENV_STATUS_OUT_OF_RESOURCES;
+                               return PXENV_EXIT_FAILURE;
+                       }
+                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+                       undi_isr->BufferLength = nic.packetlen;
+                       undi_isr->FrameLength = nic.packetlen;
+                       undi_isr->FrameHeaderLength = ETH_HLEN;
+                       memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
+                       PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
+                       switch ( ntohs(media_header->nstype) ) {
+                       case IP :       undi_isr->ProtType = P_IP;      break;
+                       case ARP :      undi_isr->ProtType = P_ARP;     break;
+                       case RARP :     undi_isr->ProtType = P_RARP;    break;
+                       default :       undi_isr->ProtType = P_UNKNOWN;
+                       }
+                       if ( memcmp ( media_header->dest, broadcast_mac,
+                                     sizeof(broadcast_mac) ) ) {
+                               undi_isr->PktType = XMT_BROADCAST;
+                       } else {
+                               undi_isr->PktType = XMT_DESTADDR;
+                       }
+                       break;
+               } else {
+                       /* No break - fall through to IN_GET_NEXT */
+               }
+       case PXENV_UNDI_ISR_IN_GET_NEXT :
+               /* We only ever return one frame at a time */
+               DBG ( " GET_NEXT DONE" );
+               /* Re-enable interrupts */
+               eth_irq ( ENABLE );
+               /* Force an interrupt if there's a packet still
+                * waiting, since we only handle one packet per
+                * interrupt.
+                */
+               if ( eth_poll ( 0 ) ) {
+                       DBG ( " (RETRIGGER)" );
+                       eth_irq ( FORCE );
+               }
+               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+               break;
+       default :
+               /* Should never happen */
+               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+               undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+               return PXENV_EXIT_FAILURE;
+       }
+
+       undi_isr->Status = PXENV_STATUS_SUCCESS;
+       return PXENV_EXIT_SUCCESS;
+}