diff --git a/src/arch/i386/image/pxe_image.c b/src/arch/i386/image/pxe_image.c index 1b611891..bd1bad35 100644 --- a/src/arch/i386/image/pxe_image.c +++ b/src/arch/i386/image/pxe_image.c @@ -13,7 +13,6 @@ #include "etherboot.h" #include "pxe_callbacks.h" -#include "pxe_export.h" #include "pxe.h" unsigned long pxe_load_offset; diff --git a/src/arch/i386/include/pxe_addr.h b/src/arch/i386/include/pxe_addr.h index b7cc6f84..954551e8 100644 --- a/src/arch/i386/include/pxe_addr.h +++ b/src/arch/i386/include/pxe_addr.h @@ -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 */ diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h index cf5a7a87..974a3c30 100644 --- a/src/arch/i386/include/pxe_callbacks.h +++ b/src/arch/i386/include/pxe_callbacks.h @@ -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 index 8b878947..00000000 --- a/src/core/pxe.c +++ /dev/null @@ -1,1445 +0,0 @@ -/* PXE API interface for Etherboot. - * - * Copyright (C) 2004 Michael Brown . - * - * 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 ( ¶ms->start_undi ); - break; - case PXENV_UNDI_STARTUP: - ret = pxenv_undi_startup ( ¶ms->undi_startup ); - break; - case PXENV_UNDI_CLEANUP: - ret = pxenv_undi_cleanup ( ¶ms->undi_cleanup ); - break; - case PXENV_UNDI_INITIALIZE: - ret = pxenv_undi_initialize ( ¶ms->undi_initialize ); - break; - case PXENV_UNDI_RESET_ADAPTER: - ret = pxenv_undi_reset_adapter ( ¶ms->undi_reset_adapter ); - break; - case PXENV_UNDI_SHUTDOWN: - ret = pxenv_undi_shutdown ( ¶ms->undi_shutdown ); - break; - case PXENV_UNDI_OPEN: - ret = pxenv_undi_open ( ¶ms->undi_open ); - break; - case PXENV_UNDI_CLOSE: - ret = pxenv_undi_close ( ¶ms->undi_close ); - break; - case PXENV_UNDI_TRANSMIT: - ret = pxenv_undi_transmit ( ¶ms->undi_transmit ); - break; - case PXENV_UNDI_SET_MCAST_ADDRESS: - ret = pxenv_undi_set_mcast_address ( - ¶ms->undi_set_mcast_address ); - break; - case PXENV_UNDI_SET_STATION_ADDRESS: - ret = pxenv_undi_set_station_address ( - ¶ms->undi_set_station_address ); - break; - case PXENV_UNDI_SET_PACKET_FILTER: - ret = pxenv_undi_set_packet_filter ( - ¶ms->undi_set_packet_filter ); - break; - case PXENV_UNDI_GET_INFORMATION: - ret = pxenv_undi_get_information ( - ¶ms->undi_get_information ); - break; - case PXENV_UNDI_GET_STATISTICS: - ret = pxenv_undi_get_statistics ( - ¶ms->undi_get_statistics ); - break; - case PXENV_UNDI_CLEAR_STATISTICS: - ret = pxenv_undi_clear_statistics ( - ¶ms->undi_clear_statistics ); - break; - case PXENV_UNDI_INITIATE_DIAGS: - ret = pxenv_undi_initiate_diags ( - ¶ms->undi_initiate_diags ); - break; - case PXENV_UNDI_FORCE_INTERRUPT: - ret = pxenv_undi_force_interrupt ( - ¶ms->undi_force_interrupt ); - break; - case PXENV_UNDI_GET_MCAST_ADDRESS: - ret = pxenv_undi_get_mcast_address ( - ¶ms->undi_get_mcast_address ); - break; - case PXENV_UNDI_GET_NIC_TYPE: - ret = pxenv_undi_get_nic_type ( ¶ms->undi_get_nic_type ); - break; - case PXENV_UNDI_GET_IFACE_INFO: - ret = pxenv_undi_get_iface_info ( - ¶ms->undi_get_iface_info ); - break; - case PXENV_UNDI_ISR: - ret = pxenv_undi_isr ( ¶ms->undi_isr ); - break; - case PXENV_STOP_UNDI: - ret = pxenv_stop_undi ( ¶ms->stop_undi ); - break; - case PXENV_TFTP_OPEN: - ret = pxenv_tftp_open ( ¶ms->tftp_open ); - break; - case PXENV_TFTP_CLOSE: - ret = pxenv_tftp_close ( ¶ms->tftp_close ); - break; - case PXENV_TFTP_READ: - ret = pxenv_tftp_read ( ¶ms->tftp_read ); - break; - case PXENV_TFTP_READ_FILE: - ret = pxenv_tftp_read_file ( ¶ms->tftp_read_file ); - break; - case PXENV_TFTP_GET_FSIZE: - ret = pxenv_tftp_get_fsize ( ¶ms->tftp_get_fsize ); - break; - case PXENV_UDP_OPEN: - ret = pxenv_udp_open ( ¶ms->udp_open ); - break; - case PXENV_UDP_CLOSE: - ret = pxenv_udp_close ( ¶ms->udp_close ); - break; - case PXENV_UDP_READ: - ret = pxenv_udp_read ( ¶ms->udp_read ); - break; - case PXENV_UDP_WRITE: - ret = pxenv_udp_write ( ¶ms->udp_write ); - break; - case PXENV_UNLOAD_STACK: - ret = pxenv_unload_stack ( ¶ms->unload_stack ); - break; - case PXENV_GET_CACHED_INFO: - ret = pxenv_get_cached_info ( ¶ms->get_cached_info ); - break; - case PXENV_RESTART_TFTP: - ret = pxenv_restart_tftp ( ¶ms->restart_tftp ); - break; - case PXENV_START_BASE: - ret = pxenv_start_base ( ¶ms->start_base ); - break; - case PXENV_STOP_BASE: - ret = pxenv_stop_base ( ¶ms->stop_base ); - break; - case PXENV_UNDI_LOADER: - ret = pxenv_undi_loader ( ¶ms->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 */ diff --git a/src/include/pxe.h b/src/include/pxe.h index d4c683fc..3264df0c 100644 --- a/src/include/pxe.h +++ b/src/include/pxe.h @@ -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 */ diff --git a/src/include/pxe_api.h b/src/include/pxe_api.h index 91eda5bf..60d5b30a 100644 --- a/src/include/pxe_api.h +++ b/src/include/pxe_api.h @@ -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 index 3d39e73c..00000000 --- a/src/include/pxe_export.h +++ /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 */ diff --git a/src/include/pxe_types.h b/src/include/pxe_types.h index 9b243018..80e6aa17 100644 --- a/src/include/pxe_types.h +++ b/src/include/pxe_types.h @@ -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 index 00000000..06e6121f --- /dev/null +++ b/src/interface/pxe/pxe.c @@ -0,0 +1,311 @@ +/** @file + * + * + * + */ + +/* + * Copyright (C) 2004 Michael Brown . + * + * 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 index 00000000..2539aaeb --- /dev/null +++ b/src/interface/pxe/pxe_loader.c @@ -0,0 +1,83 @@ +/** @file + * + * PXE UNDI loader + * + */ + +/* + * Copyright (C) 2004 Michael Brown . + * + * 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 index 00000000..9196fe3a --- /dev/null +++ b/src/interface/pxe/pxe_preboot.c @@ -0,0 +1,249 @@ +/** @file + * + * PXE Preboot API + * + */ + +/* PXE API interface for Etherboot. + * + * Copyright (C) 2004 Michael Brown . + * + * 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 index 00000000..bc4b6505 --- /dev/null +++ b/src/interface/pxe/pxe_tftp.c @@ -0,0 +1,199 @@ +/** @file + * + * PXE TFTP API + * + */ + +/* + * Copyright (C) 2004 Michael Brown . + * + * 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; +} diff --git a/src/interface/pxe/pxe_udp.c b/src/interface/pxe/pxe_udp.c index bf0a19f5..e818fc7f 100644 --- a/src/interface/pxe/pxe_udp.c +++ b/src/interface/pxe/pxe_udp.c @@ -5,6 +5,8 @@ */ #include "pxe.h" +#include "io.h" +#include "string.h" /* * Copyright (C) 2004 Michael Brown . @@ -25,13 +27,13 @@ */ /** - * 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 @@ -49,11 +51,16 @@ * - 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 index 00000000..3919915b --- /dev/null +++ b/src/interface/pxe/pxe_undi.c @@ -0,0 +1,538 @@ +/** @file + * + * PXE UNDI API + * + */ + +/* + * Copyright (C) 2004 Michael Brown . + * + * 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; +}