mirror of
https://github.com/xcat2/xNBA.git
synced 2025-04-08 09:01:15 +00:00
Split PXE code into preboot, udp, tftp, undi and loader units.
PXE code now compiles without errors (though it won't actually work).
This commit is contained in:
parent
13de295b56
commit
809933d9f7
@ -13,7 +13,6 @@
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "pxe_callbacks.h"
|
||||
#include "pxe_export.h"
|
||||
#include "pxe.h"
|
||||
|
||||
unsigned long pxe_load_offset;
|
||||
|
@ -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 */
|
||||
|
@ -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 );
|
||||
|
1445
src/core/pxe.c
1445
src/core/pxe.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
@ -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 */
|
||||
|
||||
|
311
src/interface/pxe/pxe.c
Normal file
311
src/interface/pxe/pxe.c
Normal file
@ -0,0 +1,311 @@
|
||||
/** @file
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
/* Global pointer to currently installed PXE stack */
|
||||
pxe_stack_t *pxe_stack = NULL;
|
||||
|
||||
/* Various startup/shutdown routines. The startup/shutdown call
|
||||
* sequence is incredibly badly defined in the Intel PXE spec, for
|
||||
* example:
|
||||
*
|
||||
* PXENV_UNDI_INITIALIZE says that the parameters used to initialize
|
||||
* the adaptor should be those supplied to the most recent
|
||||
* PXENV_UNDI_STARTUP call. PXENV_UNDI_STARTUP takes no parameters.
|
||||
*
|
||||
* PXENV_UNDI_CLEANUP says that the rest of the API will not be
|
||||
* available after making this call. Figure 3-3 ("Early UNDI API
|
||||
* usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
|
||||
* call to the supposedly now unavailable PXENV_STOP_UNDI.
|
||||
*
|
||||
* PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
|
||||
* occupied by the PXE stack. Figure 4-3 ("PXE IPL") shows a call
|
||||
* to PXENV_STOP_UNDI being made after the call to
|
||||
* PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
|
||||
* should have been freed (and, potentially, zeroed).
|
||||
*
|
||||
* Nothing, anywhere, seems to mention who's responsible for freeing
|
||||
* up the base memory allocated for the stack segment. It's not
|
||||
* even clear whether or not this is expected to be in free base
|
||||
* memory rather than claimed base memory.
|
||||
*
|
||||
* Consequently, we adopt a rather defensive strategy, designed to
|
||||
* work with any conceivable sequence of initialisation or shutdown
|
||||
* calls. We have only two things that we care about:
|
||||
*
|
||||
* 1. Have we hooked INT 1A and INT 15,E820(etc.)?
|
||||
* 2. Is the NIC initialised?
|
||||
*
|
||||
* The NIC should never be initialised without the vectors being
|
||||
* hooked, similarly the vectors should never be unhooked with the NIC
|
||||
* still initialised. We do, however, want to be able to have the
|
||||
* vectors hooked with the NIC shutdown. We therefore have three
|
||||
* possible states:
|
||||
*
|
||||
* 1. Ready to unload: interrupts unhooked, NIC shutdown.
|
||||
* 2. Midway: interrupts hooked, NIC shutdown.
|
||||
* 3. Fully ready: interrupts hooked, NIC initialised.
|
||||
*
|
||||
* We provide the three states CAN_UNLOAD, MIDWAY and READY to define
|
||||
* these, and the call pxe_ensure_state() to ensure that the stack is
|
||||
* in the specified state. All our PXE API call implementations
|
||||
* should use this call to ensure that the state is as required for
|
||||
* that PXE API call. This enables us to cope with whatever the
|
||||
* end-user's interpretation of the PXE spec may be. It even allows
|
||||
* for someone calling e.g. PXENV_START_UNDI followed by
|
||||
* PXENV_UDP_WRITE, without bothering with any of the intervening
|
||||
* calls.
|
||||
*
|
||||
* pxe_ensure_state() returns 1 for success, 0 for failure. In the
|
||||
* event of failure (which can arise from e.g. asking for state READY
|
||||
* when we don't know where our NIC is), the error code
|
||||
* PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
|
||||
* The macros ENSURE_XXX() can be used to achieve this without lots of
|
||||
* duplicated code.
|
||||
*/
|
||||
|
||||
/* pxe_[un]hook_stack are architecture-specific and provided in
|
||||
* pxe_callbacks.c
|
||||
*/
|
||||
|
||||
int pxe_initialise_nic ( void ) {
|
||||
if ( pxe_stack->state >= READY ) return 1;
|
||||
|
||||
#warning "device probing mechanism has completely changed"
|
||||
#if 0
|
||||
|
||||
/* Check if NIC is initialised. dev.disable is set to 0
|
||||
* when disable() is called, so we use this.
|
||||
*/
|
||||
if ( dev.disable ) {
|
||||
/* NIC may have been initialised independently
|
||||
* (e.g. when we set up the stack prior to calling the
|
||||
* NBP).
|
||||
*/
|
||||
pxe_stack->state = READY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we already have a NIC defined, reuse that one with
|
||||
* PROBE_AWAKE. If one was specifed via PXENV_START_UNDI, try
|
||||
* that one first. Otherwise, set PROBE_FIRST.
|
||||
*/
|
||||
|
||||
if ( dev.state.pci.dev.use_specified == 1 ) {
|
||||
dev.how_probe = PROBE_NEXT;
|
||||
DBG ( " initialising NIC specified via START_UNDI" );
|
||||
} else if ( dev.state.pci.dev.driver ) {
|
||||
DBG ( " reinitialising NIC" );
|
||||
dev.how_probe = PROBE_AWAKE;
|
||||
} else {
|
||||
DBG ( " probing for any NIC" );
|
||||
dev.how_probe = PROBE_FIRST;
|
||||
}
|
||||
|
||||
/* Call probe routine to bring up the NIC */
|
||||
if ( eth_probe ( &dev ) != PROBE_WORKED ) {
|
||||
DBG ( " failed" );
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
pxe_stack->state = READY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pxe_shutdown_nic ( void ) {
|
||||
if ( pxe_stack->state <= MIDWAY ) return 1;
|
||||
|
||||
eth_irq ( DISABLE );
|
||||
disable ( &dev );
|
||||
pxe_stack->state = MIDWAY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ensure_pxe_state ( pxe_stack_state_t wanted ) {
|
||||
int success = 1;
|
||||
|
||||
if ( ! pxe_stack ) return 0;
|
||||
if ( wanted >= MIDWAY )
|
||||
success = success & hook_pxe_stack();
|
||||
if ( wanted > MIDWAY ) {
|
||||
success = success & pxe_initialise_nic();
|
||||
} else {
|
||||
success = success & pxe_shutdown_nic();
|
||||
}
|
||||
if ( wanted < MIDWAY )
|
||||
success = success & unhook_pxe_stack();
|
||||
return success;
|
||||
}
|
||||
|
||||
/* API call dispatcher
|
||||
*
|
||||
* Status: complete
|
||||
*/
|
||||
PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any ) {
|
||||
PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
|
||||
|
||||
/* Set default status in case child routine fails to do so */
|
||||
any->Status = PXENV_STATUS_FAILURE;
|
||||
|
||||
DBG ( "[" );
|
||||
|
||||
/* Hand off to relevant API routine */
|
||||
switch ( opcode ) {
|
||||
case PXENV_START_UNDI:
|
||||
ret = pxenv_start_undi ( &any->start_undi );
|
||||
break;
|
||||
case PXENV_UNDI_STARTUP:
|
||||
ret = pxenv_undi_startup ( &any->undi_startup );
|
||||
break;
|
||||
case PXENV_UNDI_CLEANUP:
|
||||
ret = pxenv_undi_cleanup ( &any->undi_cleanup );
|
||||
break;
|
||||
case PXENV_UNDI_INITIALIZE:
|
||||
ret = pxenv_undi_initialize ( &any->undi_initialize );
|
||||
break;
|
||||
case PXENV_UNDI_RESET_ADAPTER:
|
||||
ret = pxenv_undi_reset_adapter ( &any->undi_reset_adapter );
|
||||
break;
|
||||
case PXENV_UNDI_SHUTDOWN:
|
||||
ret = pxenv_undi_shutdown ( &any->undi_shutdown );
|
||||
break;
|
||||
case PXENV_UNDI_OPEN:
|
||||
ret = pxenv_undi_open ( &any->undi_open );
|
||||
break;
|
||||
case PXENV_UNDI_CLOSE:
|
||||
ret = pxenv_undi_close ( &any->undi_close );
|
||||
break;
|
||||
case PXENV_UNDI_TRANSMIT:
|
||||
ret = pxenv_undi_transmit ( &any->undi_transmit );
|
||||
break;
|
||||
case PXENV_UNDI_SET_MCAST_ADDRESS:
|
||||
ret = pxenv_undi_set_mcast_address (
|
||||
&any->undi_set_mcast_address );
|
||||
break;
|
||||
case PXENV_UNDI_SET_STATION_ADDRESS:
|
||||
ret = pxenv_undi_set_station_address (
|
||||
&any->undi_set_station_address );
|
||||
break;
|
||||
case PXENV_UNDI_SET_PACKET_FILTER:
|
||||
ret = pxenv_undi_set_packet_filter (
|
||||
&any->undi_set_packet_filter );
|
||||
break;
|
||||
case PXENV_UNDI_GET_INFORMATION:
|
||||
ret = pxenv_undi_get_information (
|
||||
&any->undi_get_information );
|
||||
break;
|
||||
case PXENV_UNDI_GET_STATISTICS:
|
||||
ret = pxenv_undi_get_statistics ( &any->undi_get_statistics );
|
||||
break;
|
||||
case PXENV_UNDI_CLEAR_STATISTICS:
|
||||
ret = pxenv_undi_clear_statistics (
|
||||
&any->undi_clear_statistics );
|
||||
break;
|
||||
case PXENV_UNDI_INITIATE_DIAGS:
|
||||
ret = pxenv_undi_initiate_diags ( &any->undi_initiate_diags );
|
||||
|
||||
break;
|
||||
case PXENV_UNDI_FORCE_INTERRUPT:
|
||||
ret = pxenv_undi_force_interrupt (
|
||||
&any->undi_force_interrupt );
|
||||
break;
|
||||
case PXENV_UNDI_GET_MCAST_ADDRESS:
|
||||
ret = pxenv_undi_get_mcast_address (
|
||||
&any->undi_get_mcast_address );
|
||||
break;
|
||||
case PXENV_UNDI_GET_NIC_TYPE:
|
||||
ret = pxenv_undi_get_nic_type ( &any->undi_get_nic_type );
|
||||
break;
|
||||
case PXENV_UNDI_GET_IFACE_INFO:
|
||||
ret = pxenv_undi_get_iface_info ( &any->undi_get_iface_info );
|
||||
break;
|
||||
case PXENV_UNDI_ISR:
|
||||
ret = pxenv_undi_isr ( &any->undi_isr );
|
||||
break;
|
||||
case PXENV_STOP_UNDI:
|
||||
ret = pxenv_stop_undi ( &any->stop_undi );
|
||||
break;
|
||||
case PXENV_TFTP_OPEN:
|
||||
ret = pxenv_tftp_open ( &any->tftp_open );
|
||||
break;
|
||||
case PXENV_TFTP_CLOSE:
|
||||
ret = pxenv_tftp_close ( &any->tftp_close );
|
||||
break;
|
||||
case PXENV_TFTP_READ:
|
||||
ret = pxenv_tftp_read ( &any->tftp_read );
|
||||
break;
|
||||
case PXENV_TFTP_READ_FILE:
|
||||
ret = pxenv_tftp_read_file ( &any->tftp_read_file );
|
||||
break;
|
||||
case PXENV_TFTP_GET_FSIZE:
|
||||
ret = pxenv_tftp_get_fsize ( &any->tftp_get_fsize );
|
||||
break;
|
||||
case PXENV_UDP_OPEN:
|
||||
ret = pxenv_udp_open ( &any->udp_open );
|
||||
break;
|
||||
case PXENV_UDP_CLOSE:
|
||||
ret = pxenv_udp_close ( &any->udp_close );
|
||||
break;
|
||||
case PXENV_UDP_READ:
|
||||
ret = pxenv_udp_read ( &any->udp_read );
|
||||
break;
|
||||
case PXENV_UDP_WRITE:
|
||||
ret = pxenv_udp_write ( &any->udp_write );
|
||||
break;
|
||||
case PXENV_UNLOAD_STACK:
|
||||
ret = pxenv_unload_stack ( &any->unload_stack );
|
||||
break;
|
||||
case PXENV_GET_CACHED_INFO:
|
||||
ret = pxenv_get_cached_info ( &any->get_cached_info );
|
||||
break;
|
||||
case PXENV_RESTART_TFTP:
|
||||
ret = pxenv_restart_tftp ( &any->restart_tftp );
|
||||
break;
|
||||
case PXENV_START_BASE:
|
||||
ret = pxenv_start_base ( &any->start_base );
|
||||
break;
|
||||
case PXENV_STOP_BASE:
|
||||
ret = pxenv_stop_base ( &any->stop_base );
|
||||
break;
|
||||
|
||||
default:
|
||||
DBG ( "PXENV_UNKNOWN_%hx", opcode );
|
||||
any->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
ret = PXENV_EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( any->Status != PXENV_STATUS_SUCCESS ) {
|
||||
DBG ( " %hx", any->Status );
|
||||
}
|
||||
if ( ret != PXENV_EXIT_SUCCESS ) {
|
||||
DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
|
||||
}
|
||||
DBG ( "]" );
|
||||
|
||||
return ret;
|
||||
}
|
83
src/interface/pxe/pxe_loader.c
Normal file
83
src/interface/pxe/pxe_loader.c
Normal file
@ -0,0 +1,83 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE UNDI loader
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
/* PXENV_UNDI_LOADER
|
||||
*
|
||||
* Status: working
|
||||
*
|
||||
* NOTE: This is not a genuine PXE API call; the loader has a separate
|
||||
* entry point. However, to simplify the mapping of the PXE API to
|
||||
* the internal Etherboot API, both are directed through the same
|
||||
* interface.
|
||||
*/
|
||||
PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
|
||||
uint32_t loader_phys = virt_to_phys ( undi_loader );
|
||||
|
||||
DBG ( "PXENV_UNDI_LOADER" );
|
||||
|
||||
/* Set UNDI DS as our real-mode stack */
|
||||
use_undi_ds_for_rm_stack ( undi_loader->undi_ds );
|
||||
|
||||
/* FIXME: These lines are borrowed from main.c. There should
|
||||
* probably be a single initialise() function that does all
|
||||
* this, but it's currently split interestingly between main()
|
||||
* and main_loop()...
|
||||
*/
|
||||
|
||||
|
||||
/* CHECKME: Our init functions have probably already been
|
||||
called by the ROM prefix's call to setup(), haven't
|
||||
they? */
|
||||
|
||||
|
||||
|
||||
/* We have relocated; the loader pointer is now invalid */
|
||||
undi_loader = phys_to_virt ( loader_phys );
|
||||
|
||||
/* Install PXE stack to area specified by NBP */
|
||||
install_pxe_stack ( VIRTUAL ( undi_loader->undi_cs, 0 ) );
|
||||
|
||||
/* Call pxenv_start_undi to set parameters. Why the hell PXE
|
||||
* requires these parameters to be provided twice is beyond
|
||||
* the wit of any sane man. Don't worry if it fails; the NBP
|
||||
* should call PXENV_START_UNDI separately anyway.
|
||||
*/
|
||||
pxenv_start_undi ( &undi_loader->u.start_undi );
|
||||
/* Unhook stack; the loader is not meant to hook int 1a etc,
|
||||
* but the call the pxenv_start_undi will cause it to happen.
|
||||
*/
|
||||
|
||||
/* FIXME: can't use ENSURE_CAN_UNLOAD() thanks to newer gcc's
|
||||
* barfing on unnamed struct/unions. */
|
||||
/* ENSURE_CAN_UNLOAD ( undi_loader ); */
|
||||
|
||||
/* Fill in addresses of !PXE and PXENV+ structures */
|
||||
PTR_TO_SEGOFF16 ( &pxe_stack->pxe, undi_loader->pxe_ptr );
|
||||
PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, undi_loader->pxenv_ptr );
|
||||
|
||||
undi_loader->u.Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
249
src/interface/pxe/pxe_preboot.c
Normal file
249
src/interface/pxe/pxe_preboot.c
Normal file
@ -0,0 +1,249 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE Preboot API
|
||||
*
|
||||
*/
|
||||
|
||||
/* PXE API interface for Etherboot.
|
||||
*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
#include "pxe_callbacks.h"
|
||||
|
||||
/**
|
||||
* UNLOAD BASE CODE STACK
|
||||
*
|
||||
* @v None
|
||||
* @ret ...
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
|
||||
int success;
|
||||
|
||||
DBG ( "PXENV_UNLOAD_STACK" );
|
||||
success = ensure_pxe_state ( CAN_UNLOAD );
|
||||
|
||||
/* We need to call cleanup() at some point. The network card
|
||||
* has already been disabled by ENSURE_CAN_UNLOAD(), but for
|
||||
* the sake of completeness we should call the console_fini()
|
||||
* etc. that are part of cleanup().
|
||||
*
|
||||
* There seems to be a lack of consensus on which is the final
|
||||
* PXE API call to make, but it's a fairly safe bet that all
|
||||
* the potential shutdown sequences will include a call to
|
||||
* PXENV_UNLOAD_STACK at some point, so we may as well do it
|
||||
* here.
|
||||
*/
|
||||
cleanup();
|
||||
|
||||
if ( ! success ) {
|
||||
unload_stack->Status = PXENV_STATUS_KEEP_ALL;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
unload_stack->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_GET_CACHED_INFO
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
|
||||
*get_cached_info ) {
|
||||
BOOTPLAYER_t *cached_info = &pxe_stack->cached_info;
|
||||
DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
|
||||
ENSURE_READY ( get_cached_info );
|
||||
|
||||
/* Fill in cached_info structure in our pxe_stack */
|
||||
|
||||
/* I don't think there's actually any way we can be called in
|
||||
* the middle of a DHCP request...
|
||||
*/
|
||||
cached_info->opcode = BOOTP_REP;
|
||||
/* We only have Ethernet drivers */
|
||||
cached_info->Hardware = ETHER_TYPE;
|
||||
cached_info->Hardlen = ETH_ALEN;
|
||||
/* PXESPEC: "Client sets" says the spec, but who's filling in
|
||||
* this structure? It ain't the client.
|
||||
*/
|
||||
cached_info->Gatehops = 0;
|
||||
cached_info->ident = 0;
|
||||
cached_info->seconds = 0;
|
||||
cached_info->Flags = BOOTP_BCAST;
|
||||
/* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
|
||||
cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
/* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
|
||||
cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
|
||||
memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
|
||||
/* Nullify server name */
|
||||
cached_info->Sname[0] = '\0';
|
||||
memcpy ( cached_info->bootfile, KERNEL_BUF,
|
||||
sizeof(cached_info->bootfile) );
|
||||
/* Copy DHCP vendor options */
|
||||
memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
|
||||
sizeof(cached_info->vendor.d) );
|
||||
|
||||
/* Copy to user-specified buffer, or set pointer to our buffer */
|
||||
get_cached_info->BufferLimit = sizeof(*cached_info);
|
||||
/* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
|
||||
* 0, but what are we supposed to do with a null buffer of
|
||||
* non-zero size?!
|
||||
*/
|
||||
if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
|
||||
/* Point back to our buffer */
|
||||
PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
|
||||
get_cached_info->BufferSize = sizeof(*cached_info);
|
||||
} else {
|
||||
/* Copy to user buffer */
|
||||
size_t size = sizeof(*cached_info);
|
||||
void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
|
||||
if ( get_cached_info->BufferSize < size )
|
||||
size = get_cached_info->BufferSize;
|
||||
DBG ( " to %x", virt_to_phys ( buffer ) );
|
||||
memcpy ( buffer, cached_info, size );
|
||||
/* PXESPEC: Should we return an error if the user
|
||||
* buffer is too small? We do return the actual size
|
||||
* of the buffer via BufferLimit, so the user does
|
||||
* have a way to detect this already.
|
||||
*/
|
||||
}
|
||||
|
||||
get_cached_info->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_RESTART_TFTP
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
|
||||
*restart_tftp ) {
|
||||
PXENV_EXIT_t tftp_exit;
|
||||
|
||||
DBG ( "PXENV_RESTART_TFTP" );
|
||||
ENSURE_READY ( restart_tftp );
|
||||
|
||||
/* Words cannot describe the complete mismatch between the PXE
|
||||
* specification and any possible version of reality...
|
||||
*/
|
||||
restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
|
||||
restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
|
||||
DBG ( "(" );
|
||||
tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (union u_PXENV_ANY*)restart_tftp );
|
||||
DBG ( ")" );
|
||||
if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
|
||||
|
||||
/* Fire up the new NBP */
|
||||
restart_tftp->Status = xstartpxe();
|
||||
|
||||
/* Not sure what "SUCCESS" actually means, since we can only
|
||||
* return if the new NBP failed to boot...
|
||||
*/
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_START_UNDI
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
|
||||
unsigned char bus, devfn;
|
||||
|
||||
DBG ( "PXENV_START_UNDI" );
|
||||
ENSURE_MIDWAY(start_undi);
|
||||
|
||||
/* Record PCI bus & devfn passed by caller, so we know which
|
||||
* NIC they want to use.
|
||||
*
|
||||
* If they don't match our already-existing NIC structure, set
|
||||
* values to ensure that the specified NIC is used at the next
|
||||
* call to pxe_intialise_nic().
|
||||
*/
|
||||
bus = ( start_undi->AX >> 8 ) & 0xff;
|
||||
devfn = start_undi->AX & 0xff;
|
||||
|
||||
#warning "device probing mechanism has completely changed"
|
||||
#if 0
|
||||
if ( ( pci->dev.driver == NULL ) ||
|
||||
( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
|
||||
/* This is quite a bit of a hack and relies on
|
||||
* knowledge of the internal operation of Etherboot's
|
||||
* probe mechanism.
|
||||
*/
|
||||
DBG ( " set PCI %hhx:%hhx.%hhx",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
|
||||
dev->type = BOOT_NIC;
|
||||
dev->to_probe = PROBE_PCI;
|
||||
memset ( &dev->state, 0, sizeof(dev->state) );
|
||||
pci->advance = 1;
|
||||
pci->dev.use_specified = 1;
|
||||
pci->dev.bus = bus;
|
||||
pci->dev.devfn = devfn;
|
||||
}
|
||||
#endif
|
||||
|
||||
start_undi->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_STOP_UNDI
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
|
||||
DBG ( "PXENV_STOP_UNDI" );
|
||||
|
||||
if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
|
||||
stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stop_undi->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_START_BASE
|
||||
*
|
||||
* Status: won't implement (requires major structural changes)
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
|
||||
DBG ( "PXENV_START_BASE" );
|
||||
/* ENSURE_READY ( start_base ); */
|
||||
start_base->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_STOP_BASE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
|
||||
DBG ( "PXENV_STOP_BASE" );
|
||||
|
||||
/* The only time we will be called is when the NBP is trying
|
||||
* to shut down the PXE stack. There's nothing we need to do
|
||||
* in this call.
|
||||
*/
|
||||
|
||||
stop_base->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
199
src/interface/pxe/pxe_tftp.c
Normal file
199
src/interface/pxe/pxe_tftp.c
Normal file
@ -0,0 +1,199 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE TFTP API
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
/* PXENV_TFTP_OPEN
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
|
||||
struct sockaddr_in tftp_server;
|
||||
struct tftpreq_info_t request;
|
||||
struct tftpblk_info_t block;
|
||||
|
||||
DBG ( "PXENV_TFTP_OPEN" );
|
||||
ENSURE_READY ( tftp_open );
|
||||
|
||||
/* Set server address and port */
|
||||
tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
|
||||
? tftp_open->ServerIPAddress
|
||||
: arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
|
||||
#ifdef WORK_AROUND_BPBATCH_BUG
|
||||
/* Force use of port 69; BpBatch tries to use port 4 for some
|
||||
* bizarre reason. */
|
||||
tftp_server.sin_port = TFTP_PORT;
|
||||
#endif
|
||||
/* Ignore gateway address; we can route properly */
|
||||
/* Fill in request structure */
|
||||
request.server = &tftp_server;
|
||||
request.name = tftp_open->FileName;
|
||||
request.blksize = tftp_open->PacketSize;
|
||||
DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
|
||||
tftp_open->TFTPPort, request.name, request.blksize );
|
||||
if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
|
||||
/* Make request and get first packet */
|
||||
if ( !tftp_block ( &request, &block ) ) {
|
||||
tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
/* Fill in PacketSize */
|
||||
tftp_open->PacketSize = request.blksize;
|
||||
/* Store first block for later retrieval by TFTP_READ */
|
||||
pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
|
||||
pxe_stack->tftpdata.len = block.len;
|
||||
pxe_stack->tftpdata.eof = block.eof;
|
||||
memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
|
||||
|
||||
tftp_open->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_TFTP_CLOSE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
|
||||
DBG ( "PXENV_TFTP_CLOSE" );
|
||||
ENSURE_READY ( tftp_close );
|
||||
tftp_close->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_TFTP_READ
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
|
||||
struct tftpblk_info_t block;
|
||||
|
||||
DBG ( "PXENV_TFTP_READ" );
|
||||
ENSURE_READY ( tftp_read );
|
||||
|
||||
/* Do we have a block pending */
|
||||
if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
|
||||
block.data = pxe_stack->tftpdata.data;
|
||||
block.len = pxe_stack->tftpdata.len;
|
||||
block.eof = pxe_stack->tftpdata.eof;
|
||||
block.block = 1; /* Will be the first block */
|
||||
pxe_stack->tftpdata.magic_cookie = 0;
|
||||
} else {
|
||||
if ( !tftp_block ( NULL, &block ) ) {
|
||||
tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return data */
|
||||
tftp_read->PacketNumber = block.block;
|
||||
tftp_read->BufferSize = block.len;
|
||||
memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
|
||||
DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
|
||||
tftp_read->Buffer.offset );
|
||||
|
||||
tftp_read->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_TFTP_READ_FILE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
|
||||
int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused,
|
||||
unsigned int len, int eof ) {
|
||||
if ( pxe_stack->readfile.buffer ) {
|
||||
if ( pxe_stack->readfile.offset + len >=
|
||||
pxe_stack->readfile.bufferlen ) return -1;
|
||||
memcpy ( pxe_stack->readfile.buffer +
|
||||
pxe_stack->readfile.offset, data, len );
|
||||
}
|
||||
pxe_stack->readfile.offset += len;
|
||||
return eof ? 0 : 1;
|
||||
}
|
||||
|
||||
PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
|
||||
*tftp_read_file ) {
|
||||
struct sockaddr_in tftp_server;
|
||||
int rc;
|
||||
|
||||
DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
|
||||
tftp_read_file->Buffer,
|
||||
tftp_read_file->Buffer + tftp_read_file->BufferSize );
|
||||
ENSURE_READY ( tftp_read_file );
|
||||
|
||||
/* inserted by Klaus Wittemeier */
|
||||
/* KERNEL_BUF stores the name of the last required file */
|
||||
/* This is a fix to make Microsoft Remote Install Services work (RIS) */
|
||||
memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
|
||||
/* end of insertion */
|
||||
|
||||
/* Set server address and port */
|
||||
tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
|
||||
? tftp_read_file->ServerIPAddress
|
||||
: arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
|
||||
|
||||
pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
|
||||
pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
|
||||
pxe_stack->readfile.offset = 0;
|
||||
|
||||
rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
|
||||
pxe_tftp_read_block );
|
||||
if ( rc ) {
|
||||
tftp_read_file->Status = PXENV_STATUS_FAILURE;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
tftp_read_file->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_TFTP_GET_FSIZE
|
||||
*
|
||||
* Status: working, though ugly (we actually read the whole file,
|
||||
* because it's too ugly to make Etherboot request the tsize option
|
||||
* and hand it to us).
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
|
||||
*tftp_get_fsize ) {
|
||||
int rc;
|
||||
|
||||
DBG ( "PXENV_TFTP_GET_FSIZE" );
|
||||
ENSURE_READY ( tftp_get_fsize );
|
||||
|
||||
pxe_stack->readfile.buffer = NULL;
|
||||
pxe_stack->readfile.bufferlen = 0;
|
||||
pxe_stack->readfile.offset = 0;
|
||||
|
||||
#warning "Rewrite pxenv_tftp_get_fsize, please"
|
||||
if ( rc ) {
|
||||
tftp_get_fsize->FileSize = 0;
|
||||
tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
|
||||
tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
#include "io.h"
|
||||
#include "string.h"
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
@ -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
|
||||
|
538
src/interface/pxe/pxe_undi.c
Normal file
538
src/interface/pxe/pxe_undi.c
Normal file
@ -0,0 +1,538 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE UNDI API
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
typedef struct {
|
||||
char dest[ETH_ALEN];
|
||||
char source[ETH_ALEN];
|
||||
uint16_t nstype;
|
||||
} media_header_t;
|
||||
|
||||
static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
|
||||
|
||||
/* PXENV_UNDI_STARTUP
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
|
||||
DBG ( "PXENV_UNDI_STARTUP" );
|
||||
ENSURE_MIDWAY(undi_startup);
|
||||
|
||||
undi_startup->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_CLEANUP
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
|
||||
DBG ( "PXENV_UNDI_CLEANUP" );
|
||||
ENSURE_CAN_UNLOAD ( undi_cleanup );
|
||||
|
||||
undi_cleanup->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_INITIALIZE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
|
||||
*undi_initialize ) {
|
||||
DBG ( "PXENV_UNDI_INITIALIZE" );
|
||||
ENSURE_MIDWAY ( undi_initialize );
|
||||
|
||||
undi_initialize->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_RESET_ADAPTER
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
|
||||
*undi_reset_adapter ) {
|
||||
DBG ( "PXENV_UNDI_RESET_ADAPTER" );
|
||||
|
||||
ENSURE_MIDWAY ( undi_reset_adapter );
|
||||
ENSURE_READY ( undi_reset_adapter );
|
||||
|
||||
undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_SHUTDOWN
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
|
||||
*undi_shutdown ) {
|
||||
DBG ( "PXENV_UNDI_SHUTDOWN" );
|
||||
ENSURE_MIDWAY ( undi_shutdown );
|
||||
|
||||
undi_shutdown->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_OPEN
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
|
||||
DBG ( "PXENV_UNDI_OPEN" );
|
||||
ENSURE_READY ( undi_open );
|
||||
|
||||
/* PXESPEC: This is where we choose to enable interrupts.
|
||||
* Can't actually find where we're meant to in the PXE spec,
|
||||
* but this should work.
|
||||
*/
|
||||
eth_irq ( ENABLE );
|
||||
|
||||
undi_open->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_CLOSE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
|
||||
DBG ( "PXENV_UNDI_CLOSE" );
|
||||
ENSURE_MIDWAY ( undi_close );
|
||||
|
||||
undi_close->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_TRANSMIT
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
|
||||
*undi_transmit ) {
|
||||
struct s_PXENV_UNDI_TBD *tbd;
|
||||
const char *dest;
|
||||
unsigned int type;
|
||||
unsigned int length;
|
||||
const char *data;
|
||||
media_header_t *media_header;
|
||||
|
||||
DBG ( "PXENV_UNDI_TRANSMIT" );
|
||||
ENSURE_READY ( undi_transmit );
|
||||
|
||||
/* We support only the "immediate" portion of the TBD. Who
|
||||
* knows what Intel's "engineers" were smoking when they came
|
||||
* up with the array of transmit data blocks...
|
||||
*/
|
||||
tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
|
||||
if ( tbd->DataBlkCount > 0 ) {
|
||||
undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
data = SEGOFF16_TO_PTR ( tbd->Xmit );
|
||||
length = tbd->ImmedLength;
|
||||
|
||||
/* If destination is broadcast, we need to supply the MAC address */
|
||||
if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
|
||||
dest = broadcast_mac;
|
||||
} else {
|
||||
dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
|
||||
}
|
||||
|
||||
/* We can't properly support P_UNKNOWN without rewriting all
|
||||
* the driver transmit() methods, so we cheat: if P_UNKNOWN is
|
||||
* specified we rip the destination address and type out of
|
||||
* the pre-assembled packet, then skip over the header.
|
||||
*/
|
||||
switch ( undi_transmit->Protocol ) {
|
||||
case P_IP: type = IP; break;
|
||||
case P_ARP: type = ARP; break;
|
||||
case P_RARP: type = RARP; break;
|
||||
case P_UNKNOWN:
|
||||
media_header = (media_header_t*)data;
|
||||
dest = media_header->dest;
|
||||
type = ntohs ( media_header->nstype );
|
||||
data += ETH_HLEN;
|
||||
length -= ETH_HLEN;
|
||||
break;
|
||||
default:
|
||||
undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Send the packet */
|
||||
eth_transmit ( dest, type, length, data );
|
||||
|
||||
undi_transmit->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_SET_MCAST_ADDRESS
|
||||
*
|
||||
* Status: stub (no PXE multicast support)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
|
||||
*undi_set_mcast_address ) {
|
||||
DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
|
||||
/* ENSURE_READY ( undi_set_mcast_address ); */
|
||||
undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_SET_STATION_ADDRESS
|
||||
*
|
||||
* Status: working (deliberately incomplete)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
|
||||
*undi_set_station_address ) {
|
||||
DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
|
||||
ENSURE_READY ( undi_set_station_address );
|
||||
|
||||
/* We don't offer a facility to set the MAC address; this
|
||||
* would require adding extra code to all the Etherboot
|
||||
* drivers, for very little benefit. If we're setting it to
|
||||
* the current value anyway then return success, otherwise
|
||||
* return UNSUPPORTED.
|
||||
*/
|
||||
if ( memcmp ( nic.node_addr,
|
||||
&undi_set_station_address->StationAddress,
|
||||
ETH_ALEN ) == 0 ) {
|
||||
undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_SET_PACKET_FILTER
|
||||
*
|
||||
* Status: won't implement (would require driver API changes for no
|
||||
* real benefit)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
|
||||
*undi_set_packet_filter ) {
|
||||
DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
|
||||
/* ENSURE_READY ( undi_set_packet_filter ); */
|
||||
undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_INFORMATION
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
|
||||
*undi_get_information ) {
|
||||
DBG ( "PXENV_UNDI_GET_INFORMATION" );
|
||||
ENSURE_READY ( undi_get_information );
|
||||
|
||||
undi_get_information->BaseIo = nic.ioaddr;
|
||||
undi_get_information->IntNumber = nic.irqno;
|
||||
/* Cheat: assume all cards can cope with this */
|
||||
undi_get_information->MaxTranUnit = ETH_MAX_MTU;
|
||||
/* Cheat: we only ever have Ethernet cards */
|
||||
undi_get_information->HwType = ETHER_TYPE;
|
||||
undi_get_information->HwAddrLen = ETH_ALEN;
|
||||
/* Cheat: assume card is always configured with its permanent
|
||||
* node address. This is a valid assumption within Etherboot
|
||||
* at the time of writing.
|
||||
*/
|
||||
memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
|
||||
ETH_ALEN );
|
||||
memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
|
||||
ETH_ALEN );
|
||||
undi_get_information->ROMAddress = 0;
|
||||
/* nic.rom_info->rom_segment; */
|
||||
/* We only provide the ability to receive or transmit a single
|
||||
* packet at a time. This is a bootloader, not an OS.
|
||||
*/
|
||||
undi_get_information->RxBufCt = 1;
|
||||
undi_get_information->TxBufCt = 1;
|
||||
undi_get_information->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_STATISTICS
|
||||
*
|
||||
* Status: won't implement (would require driver API changes for no
|
||||
* real benefit)
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
|
||||
*undi_get_statistics ) {
|
||||
DBG ( "PXENV_UNDI_GET_STATISTICS" );
|
||||
/* ENSURE_READY ( undi_get_statistics ); */
|
||||
undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_CLEAR_STATISTICS
|
||||
*
|
||||
* Status: won't implement (would require driver API changes for no
|
||||
* real benefit)
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
|
||||
*undi_clear_statistics ) {
|
||||
DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
|
||||
/* ENSURE_READY ( undi_clear_statistics ); */
|
||||
undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_INITIATE_DIAGS
|
||||
*
|
||||
* Status: won't implement (would require driver API changes for no
|
||||
* real benefit)
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
|
||||
*undi_initiate_diags ) {
|
||||
DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
|
||||
/* ENSURE_READY ( undi_initiate_diags ); */
|
||||
undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_FORCE_INTERRUPT
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
|
||||
*undi_force_interrupt ) {
|
||||
DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
|
||||
ENSURE_READY ( undi_force_interrupt );
|
||||
|
||||
eth_irq ( FORCE );
|
||||
undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_MCAST_ADDRESS
|
||||
*
|
||||
* Status: stub (no PXE multicast support)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
|
||||
*undi_get_mcast_address ) {
|
||||
DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
|
||||
/* ENSURE_READY ( undi_get_mcast_address ); */
|
||||
undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_NIC_TYPE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
|
||||
*undi_get_nic_type ) {
|
||||
#warning "device probing mechanism has changed completely"
|
||||
|
||||
#if 0
|
||||
struct dev *dev = &dev;
|
||||
|
||||
DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
|
||||
ENSURE_READY ( undi_get_nic_type );
|
||||
|
||||
if ( dev->to_probe == PROBE_PCI ) {
|
||||
struct pci_device *pci = &dev->state.pci.dev;
|
||||
|
||||
undi_get_nic_type->NicType = PCI_NIC;
|
||||
undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
|
||||
undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
|
||||
undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
|
||||
undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
|
||||
undi_get_nic_type->info.pci.BusDevFunc =
|
||||
( pci->bus << 8 ) | pci->devfn;
|
||||
/* Cheat: these fields are probably unnecessary, and
|
||||
* would require adding extra code to pci.c.
|
||||
*/
|
||||
undi_get_nic_type->info.pci.Prog_Intf = 0;
|
||||
undi_get_nic_type->info.pci.Rev = 0;
|
||||
undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
|
||||
undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
|
||||
} else if ( dev->to_probe == PROBE_ISA ) {
|
||||
/* const struct isa_driver *isa = dev->state.isa.driver; */
|
||||
|
||||
undi_get_nic_type->NicType = PnP_NIC;
|
||||
/* Don't think anything fills these fields in, and
|
||||
* probably no-one will ever be interested in them.
|
||||
*/
|
||||
undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
|
||||
undi_get_nic_type->info.pnp.Base_Class = 0;
|
||||
undi_get_nic_type->info.pnp.Sub_Class = 0;
|
||||
undi_get_nic_type->info.pnp.Prog_Intf = 0;
|
||||
undi_get_nic_type->info.pnp.CardSelNum = 0;
|
||||
} else {
|
||||
/* PXESPEC: There doesn't seem to be an "unknown type"
|
||||
* defined.
|
||||
*/
|
||||
undi_get_nic_type->NicType = 0;
|
||||
}
|
||||
undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_IFACE_INFO
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
|
||||
*undi_get_iface_info ) {
|
||||
DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
|
||||
ENSURE_READY ( undi_get_iface_info );
|
||||
|
||||
/* Just hand back some info, doesn't really matter what it is.
|
||||
* Most PXE stacks seem to take this approach.
|
||||
*/
|
||||
sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
|
||||
undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
|
||||
undi_get_iface_info->ServiceFlags = 0;
|
||||
memset ( undi_get_iface_info->Reserved, 0,
|
||||
sizeof(undi_get_iface_info->Reserved) );
|
||||
undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_STATE
|
||||
*
|
||||
* Status: impossible
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
|
||||
*undi_get_state ) {
|
||||
undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
};
|
||||
|
||||
/* PXENV_UNDI_ISR
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
||||
media_header_t *media_header = (media_header_t*)nic.packet;
|
||||
|
||||
DBG ( "PXENV_UNDI_ISR" );
|
||||
/* We can't call ENSURE_READY, because this could be being
|
||||
* called as part of an interrupt service routine. Instead,
|
||||
* we should simply die if we're not READY.
|
||||
*/
|
||||
if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
|
||||
undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Just in case some idiot actually looks at these fields when
|
||||
* we weren't meant to fill them in...
|
||||
*/
|
||||
undi_isr->BufferLength = 0;
|
||||
undi_isr->FrameLength = 0;
|
||||
undi_isr->FrameHeaderLength = 0;
|
||||
undi_isr->ProtType = 0;
|
||||
undi_isr->PktType = 0;
|
||||
|
||||
switch ( undi_isr->FuncFlag ) {
|
||||
case PXENV_UNDI_ISR_IN_START :
|
||||
/* Is there a packet waiting? If so, disable
|
||||
* interrupts on the NIC and return "it's ours". Do
|
||||
* *not* necessarily acknowledge the interrupt; this
|
||||
* can happen later when eth_poll(1) is called. As
|
||||
* long as the interrupt is masked off so that it
|
||||
* doesn't immediately retrigger the 8259A then all
|
||||
* should be well.
|
||||
*/
|
||||
DBG ( " START" );
|
||||
if ( eth_poll ( 0 ) ) {
|
||||
DBG ( " OURS" );
|
||||
eth_irq ( DISABLE );
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
|
||||
} else {
|
||||
DBG ( " NOT_OURS" );
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
|
||||
}
|
||||
break;
|
||||
case PXENV_UNDI_ISR_IN_PROCESS :
|
||||
/* Call poll(), return packet. If no packet, return "done".
|
||||
*/
|
||||
DBG ( " PROCESS" );
|
||||
if ( eth_poll ( 1 ) ) {
|
||||
DBG ( " RECEIVE %d", nic.packetlen );
|
||||
if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
|
||||
/* Should never happen */
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
||||
undi_isr->Status =
|
||||
PXENV_STATUS_OUT_OF_RESOURCES;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
|
||||
undi_isr->BufferLength = nic.packetlen;
|
||||
undi_isr->FrameLength = nic.packetlen;
|
||||
undi_isr->FrameHeaderLength = ETH_HLEN;
|
||||
memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
|
||||
PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
|
||||
switch ( ntohs(media_header->nstype) ) {
|
||||
case IP : undi_isr->ProtType = P_IP; break;
|
||||
case ARP : undi_isr->ProtType = P_ARP; break;
|
||||
case RARP : undi_isr->ProtType = P_RARP; break;
|
||||
default : undi_isr->ProtType = P_UNKNOWN;
|
||||
}
|
||||
if ( memcmp ( media_header->dest, broadcast_mac,
|
||||
sizeof(broadcast_mac) ) ) {
|
||||
undi_isr->PktType = XMT_BROADCAST;
|
||||
} else {
|
||||
undi_isr->PktType = XMT_DESTADDR;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
/* No break - fall through to IN_GET_NEXT */
|
||||
}
|
||||
case PXENV_UNDI_ISR_IN_GET_NEXT :
|
||||
/* We only ever return one frame at a time */
|
||||
DBG ( " GET_NEXT DONE" );
|
||||
/* Re-enable interrupts */
|
||||
eth_irq ( ENABLE );
|
||||
/* Force an interrupt if there's a packet still
|
||||
* waiting, since we only handle one packet per
|
||||
* interrupt.
|
||||
*/
|
||||
if ( eth_poll ( 0 ) ) {
|
||||
DBG ( " (RETRIGGER)" );
|
||||
eth_irq ( FORCE );
|
||||
}
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
||||
break;
|
||||
default :
|
||||
/* Should never happen */
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
||||
undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
undi_isr->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user