mirror of https://github.com/ipxe/ipxe.git
Updated PXE UDP implementation to use the new Etherboot UDP API.
Updated PXE API dispatcher to use copy_{to,from}_user, and moved to arch/i386 since the implementation is quite architecture-dependent. (The individual PXE API calls can be largely architecture-independent.)pull/1/head
parent
e24a6cb525
commit
a0a872f7f1
|
@ -7,6 +7,7 @@ SRCDIRS += arch/i386/drivers/bus
|
|||
SRCDIRS += arch/i386/drivers/net
|
||||
SRCDIRS += arch/i386/drivers/disk
|
||||
SRCDIRS += arch/i386/interface/pcbios
|
||||
SRCDIRS += arch/i386/interface/pxe
|
||||
|
||||
# The various xxx_loader.c files are #included into core/loader.c and
|
||||
# should not be compiled directly.
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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 <gpxe/uaccess.h>
|
||||
#include <registers.h>
|
||||
#include <pxe.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* PXE API entry point
|
||||
*/
|
||||
|
||||
/** A function pointer to hold any PXE API call
|
||||
*
|
||||
* Used by pxe_api_call() to avoid large swathes of duplicated code.
|
||||
*/
|
||||
union pxenv_call {
|
||||
PXENV_EXIT_t ( * any ) ( union u_PXENV_ANY * );
|
||||
PXENV_EXIT_t ( * unknown ) ( struct s_PXENV_UNKNOWN * );
|
||||
PXENV_EXIT_t ( * unload_stack ) ( struct s_PXENV_UNLOAD_STACK * );
|
||||
PXENV_EXIT_t ( * get_cached_info )
|
||||
( struct s_PXENV_GET_CACHED_INFO * );
|
||||
PXENV_EXIT_t ( * restart_tftp ) ( struct s_PXENV_TFTP_READ_FILE * );
|
||||
PXENV_EXIT_t ( * start_undi ) ( struct s_PXENV_START_UNDI * );
|
||||
PXENV_EXIT_t ( * stop_undi ) ( struct s_PXENV_STOP_UNDI * );
|
||||
PXENV_EXIT_t ( * start_base ) ( struct s_PXENV_START_BASE * );
|
||||
PXENV_EXIT_t ( * stop_base ) ( struct s_PXENV_STOP_BASE * );
|
||||
PXENV_EXIT_t ( * tftp_open ) ( struct s_PXENV_TFTP_OPEN * );
|
||||
PXENV_EXIT_t ( * tftp_close ) ( struct s_PXENV_TFTP_CLOSE * );
|
||||
PXENV_EXIT_t ( * tftp_read ) ( struct s_PXENV_TFTP_READ * );
|
||||
PXENV_EXIT_t ( * tftp_read_file ) ( struct s_PXENV_TFTP_READ_FILE * );
|
||||
PXENV_EXIT_t ( * tftp_get_fsize ) ( struct s_PXENV_TFTP_GET_FSIZE * );
|
||||
PXENV_EXIT_t ( * udp_open ) ( struct s_PXENV_UDP_OPEN * );
|
||||
PXENV_EXIT_t ( * udp_close ) ( struct s_PXENV_UDP_CLOSE * );
|
||||
PXENV_EXIT_t ( * udp_write ) ( struct s_PXENV_UDP_WRITE * );
|
||||
PXENV_EXIT_t ( * udp_read ) ( struct s_PXENV_UDP_READ * );
|
||||
PXENV_EXIT_t ( * undi_startup ) ( struct s_PXENV_UNDI_STARTUP * );
|
||||
PXENV_EXIT_t ( * undi_cleanup ) ( struct s_PXENV_UNDI_CLEANUP * );
|
||||
PXENV_EXIT_t ( * undi_initialize )
|
||||
( struct s_PXENV_UNDI_INITIALIZE * );
|
||||
PXENV_EXIT_t ( * undi_reset_adapter ) ( struct s_PXENV_UNDI_RESET * );
|
||||
PXENV_EXIT_t ( * undi_shutdown ) ( struct s_PXENV_UNDI_SHUTDOWN * );
|
||||
PXENV_EXIT_t ( * undi_open ) ( struct s_PXENV_UNDI_OPEN * );
|
||||
PXENV_EXIT_t ( * undi_close ) ( struct s_PXENV_UNDI_CLOSE * );
|
||||
PXENV_EXIT_t ( * undi_transmit ) ( struct s_PXENV_UNDI_TRANSMIT * );
|
||||
PXENV_EXIT_t ( * undi_set_mcast_address )
|
||||
( struct s_PXENV_UNDI_SET_MCAST_ADDRESS * );
|
||||
PXENV_EXIT_t ( * undi_set_station_address )
|
||||
( struct s_PXENV_UNDI_SET_STATION_ADDRESS * );
|
||||
PXENV_EXIT_t ( * undi_set_packet_filter )
|
||||
( struct s_PXENV_UNDI_SET_PACKET_FILTER * );
|
||||
PXENV_EXIT_t ( * undi_get_information )
|
||||
( struct s_PXENV_UNDI_GET_INFORMATION * );
|
||||
PXENV_EXIT_t ( * undi_get_statistics )
|
||||
( struct s_PXENV_UNDI_GET_STATISTICS * );
|
||||
PXENV_EXIT_t ( * undi_clear_statistics )
|
||||
( struct s_PXENV_UNDI_CLEAR_STATISTICS * );
|
||||
PXENV_EXIT_t ( * undi_initiate_diags )
|
||||
( struct s_PXENV_UNDI_INITIATE_DIAGS * );
|
||||
PXENV_EXIT_t ( * undi_force_interrupt )
|
||||
( struct s_PXENV_UNDI_FORCE_INTERRUPT * );
|
||||
PXENV_EXIT_t ( * undi_get_mcast_address )
|
||||
( struct s_PXENV_UNDI_GET_MCAST_ADDRESS * );
|
||||
PXENV_EXIT_t ( * undi_get_nic_type )
|
||||
( struct s_PXENV_UNDI_GET_NIC_TYPE * );
|
||||
PXENV_EXIT_t ( * undi_get_iface_info )
|
||||
( struct s_PXENV_UNDI_GET_IFACE_INFO * );
|
||||
PXENV_EXIT_t ( * undi_get_state ) ( struct s_PXENV_UNDI_GET_STATE * );
|
||||
PXENV_EXIT_t ( * undi_isr ) ( struct s_PXENV_UNDI_ISR * );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an unknown PXE API call
|
||||
*
|
||||
* @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
|
||||
* @ret #PXENV_EXIT_FAILURE Always
|
||||
* @err #PXENV_STATUS_UNSUPPORTED Always
|
||||
*/
|
||||
static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
|
||||
pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch PXE API call
|
||||
*
|
||||
* @v bx PXE opcode
|
||||
* @v es:di Address of PXE parameter block
|
||||
* @ret ax PXE exit code
|
||||
*/
|
||||
void pxe_api_call ( struct i386_all_regs *ix86 ) {
|
||||
int opcode = ix86->regs.bx;
|
||||
userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di );
|
||||
size_t param_len;
|
||||
union u_PXENV_ANY pxenv_any;
|
||||
union pxenv_call pxenv_call;
|
||||
PXENV_EXIT_t ret;
|
||||
|
||||
switch ( opcode ) {
|
||||
case PXENV_UNLOAD_STACK:
|
||||
pxenv_call.unload_stack = pxenv_unload_stack;
|
||||
param_len = sizeof ( pxenv_any.unload_stack );
|
||||
break;
|
||||
case PXENV_GET_CACHED_INFO:
|
||||
pxenv_call.get_cached_info = pxenv_get_cached_info;
|
||||
param_len = sizeof ( pxenv_any.get_cached_info );
|
||||
break;
|
||||
case PXENV_RESTART_TFTP:
|
||||
pxenv_call.restart_tftp = pxenv_restart_tftp;
|
||||
param_len = sizeof ( pxenv_any.restart_tftp );
|
||||
break;
|
||||
case PXENV_START_UNDI:
|
||||
pxenv_call.start_undi = pxenv_start_undi;
|
||||
param_len = sizeof ( pxenv_any.start_undi );
|
||||
break;
|
||||
case PXENV_STOP_UNDI:
|
||||
pxenv_call.stop_undi = pxenv_stop_undi;
|
||||
param_len = sizeof ( pxenv_any.stop_undi );
|
||||
break;
|
||||
case PXENV_START_BASE:
|
||||
pxenv_call.start_base = pxenv_start_base;
|
||||
param_len = sizeof ( pxenv_any.start_base );
|
||||
break;
|
||||
case PXENV_STOP_BASE:
|
||||
pxenv_call.stop_base = pxenv_stop_base;
|
||||
param_len = sizeof ( pxenv_any.stop_base );
|
||||
break;
|
||||
case PXENV_TFTP_OPEN:
|
||||
pxenv_call.tftp_open = pxenv_tftp_open;
|
||||
param_len = sizeof ( pxenv_any.tftp_open );
|
||||
break;
|
||||
case PXENV_TFTP_CLOSE:
|
||||
pxenv_call.tftp_close = pxenv_tftp_close;
|
||||
param_len = sizeof ( pxenv_any.tftp_close );
|
||||
break;
|
||||
case PXENV_TFTP_READ:
|
||||
pxenv_call.tftp_read = pxenv_tftp_read;
|
||||
param_len = sizeof ( pxenv_any.tftp_read );
|
||||
break;
|
||||
case PXENV_TFTP_READ_FILE:
|
||||
pxenv_call.tftp_read_file = pxenv_tftp_read_file;
|
||||
param_len = sizeof ( pxenv_any.tftp_read_file );
|
||||
break;
|
||||
case PXENV_TFTP_GET_FSIZE:
|
||||
pxenv_call.tftp_get_fsize = pxenv_tftp_get_fsize;
|
||||
param_len = sizeof ( pxenv_any.tftp_get_fsize );
|
||||
break;
|
||||
case PXENV_UDP_OPEN:
|
||||
pxenv_call.udp_open = pxenv_udp_open;
|
||||
param_len = sizeof ( pxenv_any.udp_open );
|
||||
break;
|
||||
case PXENV_UDP_CLOSE:
|
||||
pxenv_call.udp_close = pxenv_udp_close;
|
||||
param_len = sizeof ( pxenv_any.udp_close );
|
||||
break;
|
||||
case PXENV_UDP_WRITE:
|
||||
pxenv_call.udp_write = pxenv_udp_write;
|
||||
param_len = sizeof ( pxenv_any.udp_write );
|
||||
break;
|
||||
case PXENV_UDP_READ:
|
||||
pxenv_call.udp_read = pxenv_udp_read;
|
||||
param_len = sizeof ( pxenv_any.udp_read );
|
||||
break;
|
||||
case PXENV_UNDI_STARTUP:
|
||||
pxenv_call.undi_startup = pxenv_undi_startup;
|
||||
param_len = sizeof ( pxenv_any.undi_startup );
|
||||
break;
|
||||
case PXENV_UNDI_CLEANUP:
|
||||
pxenv_call.undi_cleanup = pxenv_undi_cleanup;
|
||||
param_len = sizeof ( pxenv_any.undi_cleanup );
|
||||
break;
|
||||
case PXENV_UNDI_INITIALIZE:
|
||||
pxenv_call.undi_initialize = pxenv_undi_initialize;
|
||||
param_len = sizeof ( pxenv_any.undi_initialize );
|
||||
break;
|
||||
case PXENV_UNDI_RESET_ADAPTER:
|
||||
pxenv_call.undi_reset_adapter = pxenv_undi_reset_adapter;
|
||||
param_len = sizeof ( pxenv_any.undi_reset_adapter );
|
||||
break;
|
||||
case PXENV_UNDI_SHUTDOWN:
|
||||
pxenv_call.undi_shutdown = pxenv_undi_shutdown;
|
||||
param_len = sizeof ( pxenv_any.undi_shutdown );
|
||||
break;
|
||||
case PXENV_UNDI_OPEN:
|
||||
pxenv_call.undi_open = pxenv_undi_open;
|
||||
param_len = sizeof ( pxenv_any.undi_open );
|
||||
break;
|
||||
case PXENV_UNDI_CLOSE:
|
||||
pxenv_call.undi_close = pxenv_undi_close;
|
||||
param_len = sizeof ( pxenv_any.undi_close );
|
||||
break;
|
||||
case PXENV_UNDI_TRANSMIT:
|
||||
pxenv_call.undi_transmit = pxenv_undi_transmit;
|
||||
param_len = sizeof ( pxenv_any.undi_transmit );
|
||||
break;
|
||||
case PXENV_UNDI_SET_MCAST_ADDRESS:
|
||||
pxenv_call.undi_set_mcast_address =
|
||||
pxenv_undi_set_mcast_address;
|
||||
param_len = sizeof ( pxenv_any.undi_set_mcast_address );
|
||||
break;
|
||||
case PXENV_UNDI_SET_STATION_ADDRESS:
|
||||
pxenv_call.undi_set_station_address =
|
||||
pxenv_undi_set_station_address;
|
||||
param_len = sizeof ( pxenv_any.undi_set_station_address );
|
||||
break;
|
||||
case PXENV_UNDI_SET_PACKET_FILTER:
|
||||
pxenv_call.undi_set_packet_filter =
|
||||
pxenv_undi_set_packet_filter;
|
||||
param_len = sizeof ( pxenv_any.undi_set_packet_filter );
|
||||
break;
|
||||
case PXENV_UNDI_GET_INFORMATION:
|
||||
pxenv_call.undi_get_information = pxenv_undi_get_information;
|
||||
param_len = sizeof ( pxenv_any.undi_get_information );
|
||||
break;
|
||||
case PXENV_UNDI_GET_STATISTICS:
|
||||
pxenv_call.undi_get_statistics = pxenv_undi_get_statistics;
|
||||
param_len = sizeof ( pxenv_any.undi_get_statistics );
|
||||
break;
|
||||
case PXENV_UNDI_CLEAR_STATISTICS:
|
||||
pxenv_call.undi_clear_statistics = pxenv_undi_clear_statistics;
|
||||
param_len = sizeof ( pxenv_any.undi_clear_statistics );
|
||||
break;
|
||||
case PXENV_UNDI_INITIATE_DIAGS:
|
||||
pxenv_call.undi_initiate_diags = pxenv_undi_initiate_diags;
|
||||
param_len = sizeof ( pxenv_any.undi_initiate_diags );
|
||||
break;
|
||||
case PXENV_UNDI_FORCE_INTERRUPT:
|
||||
pxenv_call.undi_force_interrupt = pxenv_undi_force_interrupt;
|
||||
param_len = sizeof ( pxenv_any.undi_force_interrupt );
|
||||
break;
|
||||
case PXENV_UNDI_GET_MCAST_ADDRESS:
|
||||
pxenv_call.undi_get_mcast_address =
|
||||
pxenv_undi_get_mcast_address;
|
||||
param_len = sizeof ( pxenv_any.undi_get_mcast_address );
|
||||
break;
|
||||
case PXENV_UNDI_GET_NIC_TYPE:
|
||||
pxenv_call.undi_get_nic_type = pxenv_undi_get_nic_type;
|
||||
param_len = sizeof ( pxenv_any.undi_get_nic_type );
|
||||
break;
|
||||
case PXENV_UNDI_GET_IFACE_INFO:
|
||||
pxenv_call.undi_get_iface_info = pxenv_undi_get_iface_info;
|
||||
param_len = sizeof ( pxenv_any.undi_get_iface_info );
|
||||
break;
|
||||
case PXENV_UNDI_ISR:
|
||||
pxenv_call.undi_isr = pxenv_undi_isr;
|
||||
param_len = sizeof ( pxenv_any.undi_isr );
|
||||
break;
|
||||
default:
|
||||
DBG ( "PXENV_UNKNOWN_%hx", opcode );
|
||||
pxenv_call.unknown = pxenv_unknown;
|
||||
param_len = sizeof ( pxenv_any.unknown );
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy parameter block from caller */
|
||||
copy_from_user ( &pxenv_any, parameters, 0, param_len );
|
||||
|
||||
/* Set default status in case child routine fails to do so */
|
||||
pxenv_any.Status = PXENV_STATUS_FAILURE;
|
||||
|
||||
/* Hand off to relevant API routine */
|
||||
DBG ( "[" );
|
||||
ret = pxenv_call.any ( &pxenv_any );
|
||||
if ( pxenv_any.Status != PXENV_STATUS_SUCCESS ) {
|
||||
DBG ( " %02x", pxenv_any.Status );
|
||||
}
|
||||
if ( ret != PXENV_EXIT_SUCCESS ) {
|
||||
DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
|
||||
}
|
||||
DBG ( "]" );
|
||||
|
||||
/* Copy modified parameter block back to caller and return */
|
||||
copy_to_user ( parameters, 0, &pxenv_any, param_len );
|
||||
ix86->regs.ax = ret;
|
||||
}
|
|
@ -64,6 +64,9 @@ struct udp_operations {
|
|||
* @v conn UDP connection
|
||||
* @v data Data
|
||||
* @v len Length of data
|
||||
* @v st_src Source address
|
||||
* @v st_dest Destination address
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * newdata ) ( struct udp_connection *conn, void *data,
|
||||
size_t len, struct sockaddr_tcpip *st_src,
|
||||
|
@ -92,8 +95,10 @@ struct udp_connection {
|
|||
*/
|
||||
|
||||
extern int udp_bind ( struct udp_connection *conn, uint16_t local_port );
|
||||
extern void udp_bind_promisc ( struct udp_connection *conn );
|
||||
extern void udp_connect ( struct udp_connection *conn,
|
||||
struct sockaddr_tcpip *peer );
|
||||
extern void udp_connect_promisc ( struct udp_connection *conn );
|
||||
extern int udp_open ( struct udp_connection *conn, uint16_t local_port );
|
||||
extern void udp_close ( struct udp_connection *conn );
|
||||
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
|
||||
#include "pxe_types.h"
|
||||
#include "pxe_api.h"
|
||||
#include "etherboot.h"
|
||||
#include "tftp.h"
|
||||
|
||||
/* Parameter block for pxenv_unknown() */
|
||||
struct s_PXENV_UNKNOWN {
|
||||
PXENV_STATUS_t Status; /**< PXE status code */
|
||||
} PACKED;
|
||||
|
||||
typedef struct s_PXENV_UNKNOWN PXENV_UNKNOWN_t;
|
||||
|
||||
/* 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,6 +19,7 @@
|
|||
union u_PXENV_ANY {
|
||||
/* Make it easy to read status for any operation */
|
||||
PXENV_STATUS_t Status;
|
||||
struct s_PXENV_UNKNOWN unknown;
|
||||
struct s_PXENV_UNLOAD_STACK unload_stack;
|
||||
struct s_PXENV_GET_CACHED_INFO get_cached_info;
|
||||
struct s_PXENV_TFTP_READ_FILE restart_tftp;
|
||||
|
@ -82,28 +88,10 @@ 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_t cached_info;
|
||||
char packet[ETH_FRAME_LEN];
|
||||
struct {
|
||||
uint32_t magic_cookie;
|
||||
unsigned int len;
|
||||
int eof;
|
||||
char data[TFTP_MAX_BLKSIZE];
|
||||
} tftpdata;
|
||||
struct {
|
||||
char *buffer;
|
||||
uint32_t offset;
|
||||
uint32_t bufferlen;
|
||||
} readfile;
|
||||
};
|
||||
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 */
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "dev.h"
|
||||
#include "pxe.h"
|
||||
|
||||
#if 0
|
||||
|
||||
/* Global pointer to currently installed PXE stack */
|
||||
pxe_stack_t *pxe_stack = NULL;
|
||||
|
||||
|
@ -162,151 +164,4 @@ int ensure_pxe_state ( pxe_stack_state_t wanted ) {
|
|||
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;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
#include "pxe.h"
|
||||
#include "pxe_callbacks.h"
|
||||
|
||||
|
@ -247,3 +249,5 @@ PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
|
|||
stop_base->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
static int pxe_tftp_read_block ( unsigned char *data, unsigned int block,
|
||||
|
@ -621,3 +623,5 @@ Note to future API designers at Intel: try to understand the
|
|||
underlying network protocol first!
|
||||
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "pxe.h"
|
||||
#include "io.h"
|
||||
#include "string.h"
|
||||
#include <string.h>
|
||||
#include <byteswap.h>
|
||||
#include <gpxe/udp.h>
|
||||
#include <gpxe/uaccess.h>
|
||||
#include <gpxe/process.h>
|
||||
#include <pxe.h>
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
|
@ -26,14 +29,116 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/** A PXE UDP connection */
|
||||
struct pxe_udp_connection {
|
||||
/** Etherboot UDP connection */
|
||||
struct udp_connection udp;
|
||||
/** "Connection is open" flag */
|
||||
int open;
|
||||
/** Current pxenv_udp_read() operation, if any */
|
||||
struct s_PXENV_UDP_READ *pxenv_udp_read;
|
||||
/** Current pxenv_udp_write() operation, if any */
|
||||
struct s_PXENV_UDP_WRITE *pxenv_udp_write;
|
||||
};
|
||||
|
||||
static inline struct pxe_udp_connection *
|
||||
udp_to_pxe ( struct udp_connection *conn ) {
|
||||
return container_of ( conn, struct pxe_udp_connection, udp );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send PXE UDP data
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v data Temporary data buffer
|
||||
* @v len Size of temporary data buffer
|
||||
*
|
||||
* Sends the packet belonging to the current pxenv_udp_write()
|
||||
* operation.
|
||||
*/
|
||||
static void pxe_udp_senddata ( struct udp_connection *conn, void *data,
|
||||
size_t len ) {
|
||||
struct pxe_udp_connection *pxe_udp = udp_to_pxe ( conn );
|
||||
struct s_PXENV_UDP_WRITE *pxenv_udp_write = pxe_udp->pxenv_udp_write;
|
||||
userptr_t buffer;
|
||||
|
||||
/* Transmit packet */
|
||||
buffer = real_to_user ( pxenv_udp_write->buffer.segment,
|
||||
pxenv_udp_write->buffer.offset );
|
||||
if ( len > pxenv_udp_write->buffer_size )
|
||||
len = pxenv_udp_write->buffer_size;
|
||||
copy_from_user ( data, buffer, 0, len );
|
||||
udp_send ( conn, data, len );
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive PXE UDP data
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v data Received data
|
||||
* @v len Length of received data
|
||||
* @v st_src Source address
|
||||
* @v st_dest Destination address
|
||||
*
|
||||
* Receives a packet as part of the current pxenv_udp_read()
|
||||
* operation.
|
||||
*/
|
||||
static int pxe_udp_newdata ( struct udp_connection *conn, void *data,
|
||||
size_t len, struct sockaddr_tcpip *st_src,
|
||||
struct sockaddr_tcpip *st_dest ) {
|
||||
struct pxe_udp_connection *pxe_udp = udp_to_pxe ( conn );
|
||||
struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
|
||||
struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
|
||||
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
|
||||
userptr_t buffer;
|
||||
|
||||
if ( ! pxenv_udp_read ) {
|
||||
DBG ( "PXE discarded UDP packet\n" );
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/* Copy packet to buffer and record length */
|
||||
buffer = real_to_user ( pxenv_udp_read->buffer.segment,
|
||||
pxenv_udp_read->buffer.offset );
|
||||
if ( len > pxenv_udp_read->buffer_size )
|
||||
len = pxenv_udp_read->buffer_size;
|
||||
copy_to_user ( buffer, 0, data, len );
|
||||
pxenv_udp_read->buffer_size = len;
|
||||
|
||||
/* Fill in source/dest information */
|
||||
assert ( sin_src->sin_family == AF_INET );
|
||||
pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
|
||||
pxenv_udp_read->s_port = sin_src->sin_port;
|
||||
assert ( sin_dest->sin_family == AF_INET );
|
||||
pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
|
||||
pxenv_udp_read->d_port = sin_dest->sin_port;
|
||||
|
||||
/* Mark as received */
|
||||
pxe_udp->pxenv_udp_read = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** PXE UDP operations */
|
||||
static struct udp_operations pxe_udp_operations = {
|
||||
.senddata = pxe_udp_senddata,
|
||||
.newdata = pxe_udp_newdata,
|
||||
};
|
||||
|
||||
/** The PXE UDP connection */
|
||||
static struct pxe_udp_connection pxe_udp = {
|
||||
.udp.udp_op = &pxe_udp_operations,
|
||||
};
|
||||
|
||||
/**
|
||||
* UDP OPEN
|
||||
*
|
||||
* @v udp_open Pointer to a struct s_PXENV_UDP_OPEN
|
||||
* @v pxenv_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 s_PXENV_UDP_OPEN::Status PXE status code
|
||||
* @err #PXENV_STATUS_UNDI_INVALID_STATE NIC could not be initialised
|
||||
* @err #PXENV_STATUS_UDP_OPEN UDP connection already open
|
||||
* @err #PXENV_STATUS_OUT_OF_RESOURCES Could not open connection
|
||||
*
|
||||
* Prepares the PXE stack for communication using pxenv_udp_write()
|
||||
* and pxenv_udp_read().
|
||||
|
@ -45,10 +150,13 @@
|
|||
* s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address
|
||||
* will remain unchanged.)
|
||||
*
|
||||
* You can only have one open UDP connection at a time. You cannot
|
||||
* have a UDP connection open at the same time as a TFTP connection.
|
||||
* (This is not strictly true for Etherboot; see the relevant @ref
|
||||
* pxe_note_udp "implementation note" for more details.)
|
||||
* You can only have one open UDP connection at a time. This is not a
|
||||
* meaningful restriction, since pxenv_udp_write() and
|
||||
* pxenv_udp_read() allow you to specify arbitrary local and remote
|
||||
* ports and an arbitrary remote address for each packet. According
|
||||
* to the PXE specifiation, you cannot have a UDP connection open at
|
||||
* the same time as a TFTP connection; this restriction does not apply
|
||||
* to Etherboot.
|
||||
*
|
||||
* On x86, you must set the s_PXE::StatusCallout field to a nonzero
|
||||
* value before calling this function in protected mode. You cannot
|
||||
|
@ -60,38 +168,52 @@
|
|||
* for this UDP connection, or retained for all future communication.
|
||||
* The latter seems more consistent with typical PXE stack behaviour.
|
||||
*
|
||||
* @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip
|
||||
* parameter.
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
|
||||
DBG ( "PXENV_UDP_OPEN" );
|
||||
ENSURE_READY ( udp_open );
|
||||
PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
|
||||
struct in_addr new_ip = { .s_addr = pxenv_udp_open->src_ip };
|
||||
|
||||
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;
|
||||
DBG ( "PXENV_UDP_OPEN" );
|
||||
|
||||
/* Check connection is not already open */
|
||||
if ( pxe_udp.open ) {
|
||||
pxenv_udp_open->Status = PXENV_STATUS_UDP_OPEN;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
udp_open->Status = PXENV_STATUS_SUCCESS;
|
||||
/* Set IP address if specified */
|
||||
if ( new_ip.s_addr ) {
|
||||
/* FIXME: actually do something here */
|
||||
DBG ( " with new IP address %s", inet_ntoa ( new_ip ) );
|
||||
}
|
||||
|
||||
/* Open UDP connection */
|
||||
if ( udp_open ( &pxe_udp.udp, 0 ) != 0 ) {
|
||||
pxenv_udp_open->Status = PXENV_STATUS_OUT_OF_RESOURCES;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
pxe_udp.open = 1;
|
||||
|
||||
pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* UDP CLOSE
|
||||
*
|
||||
* @v udp_close Pointer to a struct s_PXENV_UDP_CLOSE
|
||||
* @v pxenv_udp_close Pointer to a struct s_PXENV_UDP_CLOSE
|
||||
* @ret #PXENV_EXIT_SUCCESS Always
|
||||
* @ret s_PXENV_UDP_CLOSE::Status PXE status code
|
||||
* @err None -
|
||||
*
|
||||
* Closes a UDP "connection" opened with pxenv_udp_open().
|
||||
* Closes a UDP connection opened with pxenv_udp_open().
|
||||
*
|
||||
* You can only have one open UDP connection at a time. You cannot
|
||||
* have a UDP connection open at the same time as a TFTP connection.
|
||||
* You cannot use pxenv_udp_close() to close a TFTP connection; use
|
||||
* pxenv_tftp_close() instead. (This is not strictly true for
|
||||
* Etherboot; see the relevant @ref pxe_note_udp "implementation note"
|
||||
* for more details.)
|
||||
* pxenv_tftp_close() instead.
|
||||
*
|
||||
* On x86, you must set the s_PXE::StatusCallout field to a nonzero
|
||||
* value before calling this function in protected mode. You cannot
|
||||
|
@ -99,16 +221,27 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
|
|||
* @ref pxe_x86_pmode16 "implementation note" for more details.)
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
|
||||
PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
|
||||
DBG ( "PXENV_UDP_CLOSE" );
|
||||
udp_close->Status = PXENV_STATUS_SUCCESS;
|
||||
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_close->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_SUCCESS; /* Well, it *is* closed */
|
||||
}
|
||||
|
||||
/* Close UDP connection */
|
||||
udp_close ( &pxe_udp.udp );
|
||||
pxe_udp.open = 0;
|
||||
|
||||
pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* UDP WRITE
|
||||
*
|
||||
* @v udp_write Pointer to a struct s_PXENV_UDP_WRITE
|
||||
* @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE
|
||||
* @v s_PXENV_UDP_WRITE::ip Destination IP address
|
||||
* @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0
|
||||
* @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0
|
||||
|
@ -118,9 +251,8 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
|
|||
* @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully
|
||||
* @ret #PXENV_EXIT_FAILURE Packet could not be transmitted
|
||||
* @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 other Any error from pxenv_undi_transmit()
|
||||
* @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
|
||||
* @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
|
||||
*
|
||||
* Transmits a single UDP packet. A valid IP and UDP header will be
|
||||
* prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
|
||||
|
@ -136,116 +268,64 @@ PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
|
|||
* If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
|
||||
*
|
||||
* You must have opened a UDP connection with pxenv_udp_open() before
|
||||
* calling pxenv_udp_write(). (This is not strictly true for
|
||||
* Etherboot; see the relevant @ref pxe_note_udp "implementation note"
|
||||
* for more details.)
|
||||
* calling pxenv_udp_write().
|
||||
*
|
||||
* On x86, you must set the s_PXE::StatusCallout field to a nonzero
|
||||
* value before calling this function in protected mode. You cannot
|
||||
* call this function with a 32-bit stack segment. (See the relevant
|
||||
* @ref pxe_x86_pmode16 "implementation note" for more details.)
|
||||
*
|
||||
* @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
|
||||
* parameter.
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write ) {
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
struct udppacket *packet = (struct udppacket *)nic.packet;
|
||||
int packet_size;
|
||||
PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
|
||||
union {
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_tcpip st;
|
||||
} dest;
|
||||
|
||||
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 );
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_write->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Construct destination socket address */
|
||||
memset ( &dest, 0, sizeof ( dest ) );
|
||||
dest.sin.sin_family = AF_INET;
|
||||
dest.sin.sin_addr.s_addr = pxenv_udp_write->ip;
|
||||
dest.sin.sin_port = pxenv_udp_write->dst_port;
|
||||
|
||||
/* Set local (source) port. PXE spec says source port is 2069
|
||||
* if not specified. Really, this ought to be set at UDP open
|
||||
* time but hey, we didn't design this API.
|
||||
*/
|
||||
if ( ! pxenv_udp_write->src_port )
|
||||
pxenv_udp_write->src_port = htons ( 2069 );
|
||||
udp_bind ( &pxe_udp.udp, pxenv_udp_write->src_port );
|
||||
|
||||
/* 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 = errno;
|
||||
if ( udp_senddata ( &pxe_udp.udp ) != 0 ) {
|
||||
pxenv_udp_write->Status = PXENV_STATUS_UNDI_TRANSMIT_ERROR;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
udp_write->Status = PXENV_STATUS_SUCCESS;
|
||||
pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Utility function for pxenv_udp_read() */
|
||||
static int await_pxe_udp ( int ival __unused, void *ptr,
|
||||
unsigned short ptype __unused,
|
||||
struct iphdr *ip, struct udphdr *udp,
|
||||
struct tcphdr *tcp __unused ) {
|
||||
struct s_PXENV_UDP_READ *udp_read = (struct s_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* UDP READ
|
||||
*
|
||||
* @v udp_read Pointer to a struct s_PXENV_UDP_READ
|
||||
* @v pxenv_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
|
||||
|
@ -258,8 +338,7 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
|
|||
* @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_UDP_CLOSED UDP connection is not open
|
||||
* @err #PXENV_STATUS_FAILURE No packet was ready to read
|
||||
*
|
||||
* Receive a single UDP packet. This is a non-blocking call; if no
|
||||
|
@ -273,9 +352,7 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
|
|||
* port will be accepted and may be returned to the caller.
|
||||
*
|
||||
* You must have opened a UDP connection with pxenv_udp_open() before
|
||||
* calling pxenv_udp_read(). (This is not strictly true for
|
||||
* Etherboot; see the relevant @ref pxe_note_udp "implementation note"
|
||||
* for more details.)
|
||||
* calling pxenv_udp_read().
|
||||
*
|
||||
* On x86, you must set the s_PXE::StatusCallout field to a nonzero
|
||||
* value before calling this function in protected mode. You cannot
|
||||
|
@ -288,45 +365,40 @@ static int await_pxe_udp ( int ival __unused, void *ptr,
|
|||
* expects us to do so, and will fail if we don't.
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read ) {
|
||||
DBG ( "PXENV_UDP_READ" );
|
||||
ENSURE_READY ( udp_read );
|
||||
PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
|
||||
struct in_addr dest_ip = { .s_addr = pxenv_udp_read->dest_ip };
|
||||
uint16_t d_port = pxenv_udp_read->d_port;
|
||||
|
||||
/* 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 ) ) {
|
||||
DBG ( "PXENV_UDP_READ" );
|
||||
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_read->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
udp_read->Status = PXENV_STATUS_SUCCESS;
|
||||
/* Bind promiscuously; we will do our own filtering */
|
||||
udp_bind_promisc ( &pxe_udp.udp );
|
||||
|
||||
/* Try receiving a packet */
|
||||
pxe_udp.pxenv_udp_read = pxenv_udp_read;
|
||||
step();
|
||||
if ( pxe_udp.pxenv_udp_read ) {
|
||||
/* No packet received */
|
||||
pxe_udp.pxenv_udp_read = NULL;
|
||||
goto no_packet;
|
||||
}
|
||||
|
||||
/* Filter on destination address and/or port */
|
||||
if ( dest_ip.s_addr && ( dest_ip.s_addr != pxenv_udp_read->dest_ip ) )
|
||||
goto no_packet;
|
||||
if ( d_port && ( d_port != pxenv_udp_read->d_port ) )
|
||||
goto no_packet;
|
||||
|
||||
pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
|
||||
no_packet:
|
||||
pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/** @page pxe_notes Etherboot PXE implementation notes
|
||||
|
||||
@section pxe_note_udp The connectionless nature of UDP
|
||||
|
||||
The PXE specification states that it is possible to have only one open
|
||||
UDP or TFTP connection at any one time. Etherboot does not
|
||||
rigourously enforce this restriction, on the UNIX principle that the
|
||||
code should not prevent the user from doing stupid things, because
|
||||
that would also prevent the user from doing clever things. Since UDP
|
||||
is a connectionless protocol, it is perfectly possible to have
|
||||
multiple concurrent UDP "connections" open, provided that you take the
|
||||
multiplicity of connections into account when calling
|
||||
pxenv_udp_read(). Similarly, there is no technical reason that
|
||||
prevents you from calling pxenv_udp_write() in the middle of a TFTP
|
||||
download.
|
||||
|
||||
Etherboot will therefore never return error codes indicating "a
|
||||
connection is already open", such as #PXENV_STATUS_UDP_OPEN. If you
|
||||
want to have multiple concurrent connections, go for it (but don't
|
||||
expect your perfectly sensible code to work with any other PXE stack).
|
||||
|
||||
Since Etherboot treats UDP as the connectionless protocol that it
|
||||
really is, pxenv_udp_close() is actually a no-op, and there is no need
|
||||
to call pxenv_udp_open() before using pxenv_udp_write() or
|
||||
pxenv_udp_read().
|
||||
|
||||
*/
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
#include "pxe.h"
|
||||
|
||||
typedef struct {
|
||||
|
@ -536,3 +538,5 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
|||
undi_isr->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -66,6 +66,18 @@ void udp_connect ( struct udp_connection *conn, struct sockaddr_tcpip *peer ) {
|
|||
memcpy ( &conn->peer, peer, sizeof ( conn->peer ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect UDP connection to all remote hosts and ports
|
||||
*
|
||||
* @v conn UDP connection
|
||||
*
|
||||
* This undoes the effect of a call to udp_connect(), i.e. allows the
|
||||
* connection to receive packets from all remote hosts and ports.
|
||||
*/
|
||||
void udp_connect_promisc ( struct udp_connection *conn ) {
|
||||
memset ( &conn->peer, 0, sizeof ( conn->peer ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a local port
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue