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
Michael Brown 2006-08-02 23:08:10 +00:00
parent e24a6cb525
commit a0a872f7f1
10 changed files with 559 additions and 324 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -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 );

View File

@ -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;
@ -81,29 +87,11 @@ typedef enum {
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_state_t state;
} 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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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().
*/

View File

@ -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

View File

@ -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
*