summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe')
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_call.c509
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_entry.S216
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_errors.c103
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_file.c306
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_loader.c50
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c357
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c586
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_udp.c405
-rw-r--r--contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_undi.c791
9 files changed, 3323 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_call.c
new file mode 100644
index 0000000..66a9b1e
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_call.c
@@ -0,0 +1,509 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/uaccess.h>
+#include <gpxe/init.h>
+#include <registers.h>
+#include <biosint.h>
+#include <pxe.h>
+#include <pxe_call.h>
+
+/** @file
+ *
+ * PXE API entry point
+ */
+
+/** Vector for chaining INT 1A */
+extern struct segoff __text16 ( pxe_int_1a_vector );
+#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
+
+/** INT 1A handler */
+extern void pxe_int_1a ( void );
+
+/** INT 1A hooked flag */
+static int int_1a_hooked = 0;
+
+/** 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 * );
+ PXENV_EXIT_t ( * file_open ) ( struct s_PXENV_FILE_OPEN * );
+ PXENV_EXIT_t ( * file_close ) ( struct s_PXENV_FILE_CLOSE * );
+ PXENV_EXIT_t ( * file_select ) ( struct s_PXENV_FILE_SELECT * );
+ PXENV_EXIT_t ( * file_read ) ( struct s_PXENV_FILE_READ * );
+ PXENV_EXIT_t ( * get_file_size ) ( struct s_PXENV_GET_FILE_SIZE * );
+ PXENV_EXIT_t ( * file_exec ) ( struct s_PXENV_FILE_EXEC * );
+ PXENV_EXIT_t ( * file_api_check ) ( struct s_PXENV_FILE_API_CHECK * );
+ PXENV_EXIT_t ( * file_exit_hook ) ( struct s_PXENV_FILE_EXIT_HOOK * );
+};
+
+/**
+ * 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
+ */
+__asmcall 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;
+ case PXENV_FILE_OPEN:
+ pxenv_call.file_open = pxenv_file_open;
+ param_len = sizeof ( pxenv_any.file_open );
+ break;
+ case PXENV_FILE_CLOSE:
+ pxenv_call.file_close = pxenv_file_close;
+ param_len = sizeof ( pxenv_any.file_close );
+ break;
+ case PXENV_FILE_SELECT:
+ pxenv_call.file_select = pxenv_file_select;
+ param_len = sizeof ( pxenv_any.file_select );
+ break;
+ case PXENV_FILE_READ:
+ pxenv_call.file_read = pxenv_file_read;
+ param_len = sizeof ( pxenv_any.file_read );
+ break;
+ case PXENV_GET_FILE_SIZE:
+ pxenv_call.get_file_size = pxenv_get_file_size;
+ param_len = sizeof ( pxenv_any.get_file_size );
+ break;
+ case PXENV_FILE_EXEC:
+ pxenv_call.file_exec = pxenv_file_exec;
+ param_len = sizeof ( pxenv_any.file_exec );
+ break;
+ case PXENV_FILE_API_CHECK:
+ pxenv_call.file_api_check = pxenv_file_api_check;
+ param_len = sizeof ( pxenv_any.file_api_check );
+ break;
+ case PXENV_FILE_EXIT_HOOK:
+ pxenv_call.file_exit_hook = pxenv_file_exit_hook;
+ param_len = sizeof ( pxenv_any.file_exit_hook );
+ 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;
+}
+
+/**
+ * Dispatch weak PXE API call with PXE stack available
+ *
+ * @v ix86 Registers for PXE call
+ * @ret present Zero (PXE stack present)
+ */
+int _pxe_api_call_weak ( struct i386_all_regs *ix86 )
+{
+ pxe_api_call ( ix86 );
+ return 0;
+}
+
+/**
+ * Dispatch PXE loader call
+ *
+ * @v es:di Address of PXE parameter block
+ * @ret ax PXE exit code
+ */
+__asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
+ userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
+ struct s_UNDI_LOADER params;
+ PXENV_EXIT_t ret;
+
+ /* Copy parameter block from caller */
+ copy_from_user ( &params, uparams, 0, sizeof ( params ) );
+
+ /* Fill in ROM segment address */
+ ppxe.UNDIROMID.segment = ix86->segs.ds;
+
+ /* Set default status in case child routine fails to do so */
+ params.Status = PXENV_STATUS_FAILURE;
+
+ /* Call UNDI loader */
+ ret = undi_loader ( &params );
+
+ /* Copy modified parameter block back to caller and return */
+ copy_to_user ( uparams, 0, &params, sizeof ( params ) );
+ ix86->regs.ax = ret;
+}
+
+/**
+ * Calculate byte checksum as used by PXE
+ *
+ * @v data Data
+ * @v size Length of data
+ * @ret sum Checksum
+ */
+static uint8_t pxe_checksum ( void *data, size_t size ) {
+ uint8_t *bytes = data;
+ uint8_t sum = 0;
+
+ while ( size-- ) {
+ sum += *bytes++;
+ }
+ return sum;
+}
+
+/**
+ * Initialise !PXE and PXENV+ structures
+ *
+ */
+static void pxe_init_structures ( void ) {
+ uint32_t rm_cs_phys = ( rm_cs << 4 );
+ uint32_t rm_ds_phys = ( rm_ds << 4 );
+
+ /* Fill in missing segment fields */
+ ppxe.EntryPointSP.segment = rm_cs;
+ ppxe.EntryPointESP.segment = rm_cs;
+ ppxe.Stack.segment_address = rm_ds;
+ ppxe.Stack.Physical_address = rm_ds_phys;
+ ppxe.UNDIData.segment_address = rm_ds;
+ ppxe.UNDIData.Physical_address = rm_ds_phys;
+ ppxe.UNDICode.segment_address = rm_cs;
+ ppxe.UNDICode.Physical_address = rm_cs_phys;
+ ppxe.UNDICodeWrite.segment_address = rm_cs;
+ ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
+ pxenv.RMEntry.segment = rm_cs;
+ pxenv.StackSeg = rm_ds;
+ pxenv.UNDIDataSeg = rm_ds;
+ pxenv.UNDICodeSeg = rm_cs;
+ pxenv.PXEPtr.segment = rm_cs;
+
+ /* Update checksums */
+ ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
+ pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
+}
+
+/** PXE structure initialiser */
+struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = pxe_init_structures,
+};
+
+/**
+ * Activate PXE stack
+ *
+ * @v netdev Net device to use as PXE net device
+ */
+void pxe_activate ( struct net_device *netdev ) {
+
+ /* Ensure INT 1A is hooked */
+ if ( ! int_1a_hooked ) {
+ hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
+ &pxe_int_1a_vector );
+ int_1a_hooked = 1;
+ }
+
+ /* Set PXE network device */
+ pxe_set_netdev ( netdev );
+}
+
+/**
+ * Deactivate PXE stack
+ *
+ * @ret rc Return status code
+ */
+int pxe_deactivate ( void ) {
+ int rc;
+
+ /* Clear PXE network device */
+ pxe_set_netdev ( NULL );
+
+ /* Ensure INT 1A is unhooked, if possible */
+ if ( int_1a_hooked ) {
+ if ( ( rc = unhook_bios_interrupt ( 0x1a,
+ (unsigned int) pxe_int_1a,
+ &pxe_int_1a_vector ))!= 0){
+ DBG ( "Could not unhook INT 1A: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ int_1a_hooked = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Start PXE NBP at 0000:7c00
+ *
+ * @ret rc Return status code
+ */
+int pxe_start_nbp ( void ) {
+ int discard_b, discard_c, discard_d, discard_D;
+ uint16_t rc;
+
+ /* Far call to PXE NBP */
+ __asm__ __volatile__ ( REAL_CODE ( "movw %%cx, %%es\n\t"
+ "pushw %%es\n\t"
+ "pushw %%di\n\t"
+ "sti\n\t"
+ "lcall $0, $0x7c00\n\t"
+ "addw $4, %%sp\n\t" )
+ : "=a" ( rc ), "=b" ( discard_b ),
+ "=c" ( discard_c ), "=d" ( discard_d ),
+ "=D" ( discard_D )
+ : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
+ "c" ( rm_cs ),
+ "d" ( virt_to_phys ( &pxenv ) ),
+ "D" ( __from_text16 ( &ppxe ) )
+ : "esi", "ebp", "memory" );
+
+ return rc;
+}
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
new file mode 100644
index 0000000..0d3a57c
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+ .arch i386
+
+/****************************************************************************
+ * !PXE structure
+ ****************************************************************************
+ */
+ .section ".text16.data", "aw", @progbits
+ .globl ppxe
+ .align 16
+ppxe:
+ .ascii "!PXE" /* Signature */
+ .byte pxe_length /* StructLength */
+ .byte 0 /* StructCksum */
+ .byte 0 /* StructRev */
+ .byte 0 /* reserved_1 */
+ .word undiheader, 0 /* UNDIROMID */
+ .word 0, 0 /* BaseROMID */
+ .word pxe_entry_sp, 0 /* EntryPointSP */
+ .word pxe_entry_esp, 0 /* EntryPointESP */
+ .word -1, -1 /* StatusCallout */
+ .byte 0 /* reserved_2 */
+ .byte SegDescCnt /* SegDescCnt */
+ .word 0 /* FirstSelector */
+pxe_segments:
+ .word 0, 0, 0, _data16_memsz /* Stack */
+ .word 0, 0, 0, _data16_memsz /* UNDIData */
+ .word 0, 0, 0, _text16_memsz /* UNDICode */
+ .word 0, 0, 0, _text16_memsz /* UNDICodeWrite */
+ .word 0, 0, 0, 0 /* BC_Data */
+ .word 0, 0, 0, 0 /* BC_Code */
+ .word 0, 0, 0, 0 /* BC_CodeWrite */
+ .equ SegDescCnt, ( ( . - pxe_segments ) / 8 )
+ .equ pxe_length, . - ppxe
+ .size ppxe, . - ppxe
+
+ /* Define undiheader=0 as a weak symbol for non-ROM builds */
+ .section ".weak", "a", @nobits
+ .weak undiheader
+undiheader:
+
+/****************************************************************************
+ * PXENV+ structure
+ ****************************************************************************
+ */
+ .section ".text16.data", "aw", @progbits
+ .globl pxenv
+ .align 16
+pxenv:
+ .ascii "PXENV+" /* Signature */
+ .word 0x0201 /* Version */
+ .byte pxenv_length /* Length */
+ .byte 0 /* Checksum */
+ .word pxenv_entry, 0 /* RMEntry */
+ .long 0 /* PMEntry */
+ .word 0 /* PMSelector */
+ .word 0 /* StackSeg */
+ .word _data16_memsz /* StackSize */
+ .word 0 /* BC_CodeSeg */
+ .word 0 /* BC_CodeSize */
+ .word 0 /* BC_DataSeg */
+ .word 0 /* BC_DataSize */
+ .word 0 /* UNDIDataSeg */
+ .word _data16_memsz /* UNDIDataSize */
+ .word 0 /* UNDICodeSeg */
+ .word _text16_memsz /* UNDICodeSize */
+ .word ppxe, 0 /* PXEPtr */
+ .equ pxenv_length, . - pxenv
+ .size pxenv, . - pxenv
+
+/****************************************************************************
+ * pxenv_entry (16-bit far call)
+ *
+ * PXE API call PXENV+ entry point
+ *
+ * Parameters:
+ * %es:di : Far pointer to PXE parameter structure
+ * %bx : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ /* Wyse Streaming Manager server (WLDRM13.BIN) assumes that
+ * the PXENV+ entry point is at UNDI_CS:0000; apparently,
+ * somebody at Wyse has difficulty distinguishing between the
+ * words "may" and "must"...
+ */
+ .section ".text16.null", "ax", @progbits
+ .code16
+pxenv_null_entry:
+ jmp pxenv_entry
+
+ .section ".text16", "ax", @progbits
+ .code16
+pxenv_entry:
+ pushl $pxe_api_call
+ pushw %cs
+ call prot_call
+ addl $4, %esp
+ lret
+ .size pxenv_entry, . - pxenv_entry
+
+/****************************************************************************
+ * pxe_entry
+ *
+ * PXE API call !PXE entry point
+ *
+ * Parameters:
+ * stack : Far pointer to PXE parameter structure
+ * stack : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+pxe_entry:
+pxe_entry_sp:
+ /* Preserve original %esp */
+ pushl %esp
+ /* Zero high word of %esp to allow use of common code */
+ movzwl %sp, %esp
+ jmp pxe_entry_common
+pxe_entry_esp:
+ /* Preserve %esp to match behaviour of pxe_entry_sp */
+ pushl %esp
+pxe_entry_common:
+ /* Save PXENV+ API call registers */
+ pushw %es
+ pushw %di
+ pushw %bx
+ /* Load !PXE parameters from stack into PXENV+ registers */
+ addr32 movw 18(%esp), %bx
+ movw %bx, %es
+ addr32 movw 16(%esp), %di
+ addr32 movw 14(%esp), %bx
+ /* Make call as for PXENV+ */
+ pushw %cs
+ call pxenv_entry
+ /* Restore PXENV+ registers */
+ popw %bx
+ popw %di
+ popw %es
+ /* Restore original %esp and return */
+ popl %esp
+ lret
+ .size pxe_entry, . - pxe_entry
+
+/****************************************************************************
+ * pxe_int_1a
+ *
+ * PXE INT 1A handler
+ *
+ * Parameters:
+ * %ax : 0x5650
+ * Returns:
+ * %ax : 0x564e
+ * %es:bx : Far pointer to the PXENV+ structure
+ * %edx : Physical address of the PXENV+ structure
+ * CF cleared
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl pxe_int_1a
+pxe_int_1a:
+ pushfw
+ cmpw $0x5650, %ax
+ jne 1f
+ /* INT 1A,5650 - PXE installation check */
+ xorl %edx, %edx
+ movw %cs, %dx
+ movw %dx, %es
+ movw $pxenv, %bx
+ shll $4, %edx
+ addl $pxenv, %edx
+ movw $0x564e, %ax
+ pushw %bp
+ movw %sp, %bp
+ andb $~0x01, 8(%bp) /* Clear CF on return */
+ popw %bp
+ popfw
+ iret
+1: /* INT 1A,other - pass through */
+ popfw
+ ljmp *%cs:pxe_int_1a_vector
+
+ .section ".text16.data", "aw", @progbits
+ .globl pxe_int_1a_vector
+pxe_int_1a_vector: .long 0
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_errors.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_errors.c
new file mode 100644
index 0000000..f884ef8
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_errors.c
@@ -0,0 +1,103 @@
+#include <errno.h>
+#include <gpxe/errortab.h>
+
+/*
+ * This table was generated from the relevant section of errno.h using
+ *
+ * perl -ne 'if ( /(PXENV_STATUS_(\S+))/ ) {
+ * $code = $1; $msg = $2;
+ * $msg =~ s/_/ /g; $msg = ucfirst lc $msg;
+ * $msg =~ s/(tftp|udp|arp|undi|bis|binl|pxenv|pxe|dhcp)/uc $1/ieg;
+ * print "\t{ $code, \"$msg\" },\n";
+ * }'
+ *
+ * followed by a little manual tweaking.
+ *
+ */
+struct errortab pxe_errortab[] __errortab = {
+ { PXENV_STATUS_SUCCESS, "Success" },
+ { PXENV_STATUS_FAILURE, "Failure" },
+ { PXENV_STATUS_BAD_FUNC, "Bad function" },
+ { PXENV_STATUS_UNSUPPORTED, "Unsupported function" },
+ { PXENV_STATUS_KEEP_UNDI, "Keep UNDI" },
+ { PXENV_STATUS_KEEP_ALL, "Keep all" },
+ { PXENV_STATUS_OUT_OF_RESOURCES, "Out of resources" },
+ { PXENV_STATUS_ARP_TIMEOUT, "ARP timeout" },
+ { PXENV_STATUS_UDP_CLOSED, "UDP closed" },
+ { PXENV_STATUS_UDP_OPEN, "UDP open" },
+ { PXENV_STATUS_TFTP_CLOSED, "TFTP closed" },
+ { PXENV_STATUS_TFTP_OPEN, "TFTP open" },
+ { PXENV_STATUS_MCOPY_PROBLEM, "Memory copy problem" },
+ { PXENV_STATUS_BIS_INTEGRITY_FAILURE, "BIS integrity failure" },
+ { PXENV_STATUS_BIS_VALIDATE_FAILURE, "BIS validation failure" },
+ { PXENV_STATUS_BIS_INIT_FAILURE, "BIS init failure" },
+ { PXENV_STATUS_BIS_SHUTDOWN_FAILURE, "BIS shutdown failure" },
+ { PXENV_STATUS_BIS_GBOA_FAILURE, "BIS GBOA failure" },
+ { PXENV_STATUS_BIS_FREE_FAILURE, "BIS free failure" },
+ { PXENV_STATUS_BIS_GSI_FAILURE, "BIS GSI failure" },
+ { PXENV_STATUS_BIS_BAD_CKSUM, "BIS bad checksum" },
+ { PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS, "TFTP cannot ARP address" },
+ { PXENV_STATUS_TFTP_OPEN_TIMEOUT, "TFTP open timeout" },
+ { PXENV_STATUS_TFTP_UNKNOWN_OPCODE, "TFTP unknown opcode" },
+ { PXENV_STATUS_TFTP_READ_TIMEOUT, "TFTP read timeout" },
+ { PXENV_STATUS_TFTP_ERROR_OPCODE, "TFTP error opcode" },
+ { PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION,
+ "TFTP cannot open connection" },
+ { PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION,
+ "TFTP cannot read from connection" },
+ { PXENV_STATUS_TFTP_TOO_MANY_PACKAGES, "TFTP too many packages" },
+ { PXENV_STATUS_TFTP_FILE_NOT_FOUND, "TFTP file not found" },
+ { PXENV_STATUS_TFTP_ACCESS_VIOLATION, "TFTP access violation" },
+ { PXENV_STATUS_TFTP_NO_MCAST_ADDRESS, "TFTP no mcast address" },
+ { PXENV_STATUS_TFTP_NO_FILESIZE, "TFTP no filesize" },
+ { PXENV_STATUS_TFTP_INVALID_PACKET_SIZE, "TFTP invalid packet size" },
+ { PXENV_STATUS_DHCP_TIMEOUT, "DHCP timeout" },
+ { PXENV_STATUS_DHCP_NO_IP_ADDRESS, "DHCP no ip address" },
+ { PXENV_STATUS_DHCP_NO_BOOTFILE_NAME, "DHCP no bootfile name" },
+ { PXENV_STATUS_DHCP_BAD_IP_ADDRESS, "DHCP bad ip address" },
+ { PXENV_STATUS_UNDI_INVALID_FUNCTION, "UNDI invalid function" },
+ { PXENV_STATUS_UNDI_MEDIATEST_FAILED, "UNDI mediatest failed" },
+ { PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST,
+ "UNDI cannot initialise NIC for multicast" },
+ { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC,
+ "UNDI cannot initialise NIC" },
+ { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY,
+ "UNDI cannot initialise PHY" },
+ { PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA,
+ "UNDI cannot read config data" },
+ { PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA,
+ "UNDI cannot read init data" },
+ { PXENV_STATUS_UNDI_BAD_MAC_ADDRESS, "UNDI bad MAC address" },
+ { PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM, "UNDI bad EEPROM checksum" },
+ { PXENV_STATUS_UNDI_ERROR_SETTING_ISR, "UNDI error setting ISR" },
+ { PXENV_STATUS_UNDI_INVALID_STATE, "UNDI invalid state" },
+ { PXENV_STATUS_UNDI_TRANSMIT_ERROR, "UNDI transmit error" },
+ { PXENV_STATUS_UNDI_INVALID_PARAMETER, "UNDI invalid parameter" },
+ { PXENV_STATUS_BSTRAP_PROMPT_MENU, "Bootstrap prompt menu" },
+ { PXENV_STATUS_BSTRAP_MCAST_ADDR, "Bootstrap mcast addr" },
+ { PXENV_STATUS_BSTRAP_MISSING_LIST, "Bootstrap missing list" },
+ { PXENV_STATUS_BSTRAP_NO_RESPONSE, "Bootstrap no response" },
+ { PXENV_STATUS_BSTRAP_FILE_TOO_BIG, "Bootstrap file too big" },
+ { PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE,
+ "BINL canceled by keystroke" },
+ { PXENV_STATUS_BINL_NO_PXE_SERVER, "BINL no PXE server" },
+ { PXENV_STATUS_NOT_AVAILABLE_IN_PMODE,
+ "Not available in protected mode" },
+ { PXENV_STATUS_NOT_AVAILABLE_IN_RMODE, "Not available in real mode" },
+ { PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED,
+ "BUSD device not supported" },
+ { PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY,
+ "Loader no free base memory" },
+ { PXENV_STATUS_LOADER_NO_BC_ROMID, "Loader no Base Code ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_BC_ROMID, "Loader bad Base Code ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE,
+ "Loader bad Base Code runtime image" },
+ { PXENV_STATUS_LOADER_NO_UNDI_ROMID, "Loader no UNDI ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_UNDI_ROMID, "Loader bad UNDI ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE,
+ "Loader bad UNDI driver image" },
+ { PXENV_STATUS_LOADER_NO_PXE_STRUCT, "Loader no !PXE struct" },
+ { PXENV_STATUS_LOADER_NO_PXENV_STRUCT, "Loader no PXENV+ struct" },
+ { PXENV_STATUS_LOADER_UNDI_START, "Loader UNDI start" },
+ { PXENV_STATUS_LOADER_BC_START, "Loader Base Code start" },
+};
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_file.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_file.c
new file mode 100644
index 0000000..8d83212
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_file.c
@@ -0,0 +1,306 @@
+/** @file
+ *
+ * PXE FILE API
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/posix_io.h>
+#include <gpxe/features.h>
+#include <pxe.h>
+#include <realmode.h>
+
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ * Portions (C) 2010 Shao Miller <shao.miller@yrdsb.edu.on.ca>.
+ * [PXE exit hook logic]
+ *
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
+
+/**
+ * FILE OPEN
+ *
+ * @v file_open Pointer to a struct s_PXENV_FILE_OPEN
+ * @v s_PXENV_FILE_OPEN::FileName URL of file to open
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_FILE_OPEN::Status PXE status code
+ * @ret s_PXENV_FILE_OPEN::FileHandle Handle of opened file
+ *
+ */
+PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
+ userptr_t filename;
+ size_t filename_len;
+ int fd;
+
+ DBG ( "PXENV_FILE_OPEN" );
+
+ /* Copy name from external program, and open it */
+ filename = real_to_user ( file_open->FileName.segment,
+ file_open->FileName.offset );
+ filename_len = strlen_user ( filename, 0 );
+ {
+ char uri_string[ filename_len + 1 ];
+
+ copy_from_user ( uri_string, filename, 0,
+ sizeof ( uri_string ) );
+ DBG ( " %s", uri_string );
+ fd = open ( uri_string );
+ }
+
+ if ( fd < 0 ) {
+ file_open->Status = PXENV_STATUS ( fd );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " as file %d", fd );
+
+ file_open->FileHandle = fd;
+ file_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE CLOSE
+ *
+ * @v file_close Pointer to a struct s_PXENV_FILE_CLOSE
+ * @v s_PXENV_FILE_CLOSE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File was closed
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_FILE_CLOSE::Status PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
+
+ DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
+
+ close ( file_close->FileHandle );
+ file_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE SELECT
+ *
+ * @v file_select Pointer to a struct s_PXENV_FILE_SELECT
+ * @v s_PXENV_FILE_SELECT::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File has been checked for readiness
+ * @ret #PXENV_EXIT_FAILURE File has not been checked for readiness
+ * @ret s_PXENV_FILE_SELECT::Status PXE status code
+ * @ret s_PXENV_FILE_SELECT::Ready Indication of readiness
+ *
+ */
+PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
+ fd_set fdset;
+ int ready;
+
+ DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle );
+
+ FD_ZERO ( &fdset );
+ FD_SET ( file_select->FileHandle, &fdset );
+ if ( ( ready = select ( &fdset, 0 ) ) < 0 ) {
+ file_select->Status = PXENV_STATUS ( ready );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ file_select->Ready = ( ready ? RDY_READ : 0 );
+ file_select->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE READ
+ *
+ * @v file_read Pointer to a struct s_PXENV_FILE_READ
+ * @v s_PXENV_FILE_READ::FileHandle File handle
+ * @v s_PXENV_FILE_READ::BufferSize Size of data buffer
+ * @v s_PXENV_FILE_READ::Buffer Data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data has been read from file
+ * @ret #PXENV_EXIT_FAILURE Data has not been read from file
+ * @ret s_PXENV_FILE_READ::Status PXE status code
+ * @ret s_PXENV_FILE_READ::Ready Indication of readiness
+ * @ret s_PXENV_FILE_READ::BufferSize Length of data read
+ *
+ */
+PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
+ userptr_t buffer;
+ ssize_t len;
+
+ DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
+ file_read->Buffer.segment, file_read->Buffer.offset,
+ file_read->BufferSize );
+
+ buffer = real_to_user ( file_read->Buffer.segment,
+ file_read->Buffer.offset );
+ if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
+ file_read->BufferSize ) ) < 0 ) {
+ file_read->Status = PXENV_STATUS ( len );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " read %04zx", ( ( size_t ) len ) );
+
+ file_read->BufferSize = len;
+ file_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * GET FILE SIZE
+ *
+ * @v get_file_size Pointer to a struct s_PXENV_GET_FILE_SIZE
+ * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File size has been determined
+ * @ret #PXENV_EXIT_FAILURE File size has not been determined
+ * @ret s_PXENV_GET_FILE_SIZE::Status PXE status code
+ * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file
+ */
+PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE
+ *get_file_size ) {
+ ssize_t filesize;
+
+ DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle );
+
+ filesize = fsize ( get_file_size->FileHandle );
+ if ( filesize < 0 ) {
+ get_file_size->Status = PXENV_STATUS ( filesize );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " is %zd", ( ( size_t ) filesize ) );
+
+ get_file_size->FileSize = filesize;
+ get_file_size->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE EXEC
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_EXEC
+ * @v s_PXENV_FILE_EXEC::Command Command to execute
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_EXEC::Status PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
+ userptr_t command;
+ size_t command_len;
+ int rc;
+
+ DBG ( "PXENV_FILE_EXEC" );
+
+ /* Copy name from external program, and exec it */
+ command = real_to_user ( file_exec->Command.segment,
+ file_exec->Command.offset );
+ command_len = strlen_user ( command, 0 );
+ {
+ char command_string[ command_len + 1 ];
+
+ copy_from_user ( command_string, command, 0,
+ sizeof ( command_string ) );
+ DBG ( " %s", command_string );
+
+ if ( ( rc = system ( command_string ) ) != 0 ) {
+ file_exec->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ file_exec->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+segoff_t __data16 ( pxe_exit_hook ) = { 0, 0 };
+#define pxe_exit_hook __use_data16 ( pxe_exit_hook )
+
+/**
+ * FILE API CHECK
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_API_CHECK
+ * @v s_PXENV_FILE_API_CHECK::Magic Inbound magic number (0x91d447b2)
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_API_CHECK::Status PXE status code
+ * @ret s_PXENV_FILE_API_CHECK::Magic Outbound magic number (0xe9c17b20)
+ * @ret s_PXENV_FILE_API_CHECK::Provider "gPXE" (0x45585067)
+ * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
+ * @ret s_PXENV_FILE_API_CHECK::Flags Reserved
+ *
+ */
+PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
+ DBG ( "PXENV_FILE_API_CHECK" );
+
+ if ( file_api_check->Magic != 0x91d447b2 ) {
+ file_api_check->Status = PXENV_STATUS_BAD_FUNC;
+ return PXENV_EXIT_FAILURE;
+ } else if ( file_api_check->Size <
+ sizeof(struct s_PXENV_FILE_API_CHECK) ) {
+ file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ } else {
+ file_api_check->Status = PXENV_STATUS_SUCCESS;
+ file_api_check->Size = sizeof(struct s_PXENV_FILE_API_CHECK);
+ file_api_check->Magic = 0xe9c17b20;
+ file_api_check->Provider = 0x45585067; /* "gPXE" */
+ file_api_check->APIMask = 0x0000007f; /* Functions e0-e6 */
+ /* Check to see if we have a PXE exit hook */
+ if ( pxe_exit_hook.segment | pxe_exit_hook.offset )
+ /* Function e7, also */
+ file_api_check->APIMask |= 0x00000080;
+ file_api_check->Flags = 0; /* None defined */
+ return PXENV_EXIT_SUCCESS;
+ }
+}
+
+/**
+ * FILE EXIT HOOK
+ *
+ * @v file_exit_hook Pointer to a struct
+ * s_PXENV_FILE_EXIT_HOOK
+ * @v s_PXENV_FILE_EXIT_HOOK::Hook SEG16:OFF16 to jump to
+ * @ret #PXENV_EXIT_SUCCESS Successfully set hook
+ * @ret #PXENV_EXIT_FAILURE We're not an NBP build
+ * @ret s_PXENV_FILE_EXIT_HOOK::Status PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_exit_hook ( struct s_PXENV_FILE_EXIT_HOOK
+ *file_exit_hook ) {
+ DBG ( "PXENV_FILE_EXIT_HOOK" );
+
+ /* Check to see if we have a PXE exit hook */
+ if ( pxe_exit_hook.segment | pxe_exit_hook.offset ) {
+ /* We'll jump to the specified SEG16:OFF16 during exit */
+ pxe_exit_hook.segment = file_exit_hook->Hook.segment;
+ pxe_exit_hook.offset = file_exit_hook->Hook.offset;
+ file_exit_hook->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+ }
+
+ DBG ( " not NBP" );
+ file_exit_hook->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_loader.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_loader.c
new file mode 100644
index 0000000..b35caf7
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_loader.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/init.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/** @file
+ *
+ * PXE UNDI loader
+ *
+ */
+
+/* PXENV_UNDI_LOADER
+ *
+ */
+PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
+
+ /* Perform one-time initialisation (e.g. heap) */
+ initialise();
+
+ DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
+ undi_loader->UNDI_CS, undi_loader->UNDI_DS );
+
+ /* Fill in UNDI loader structure */
+ undi_loader->PXEptr.segment = rm_cs;
+ undi_loader->PXEptr.offset = __from_text16 ( &ppxe );
+ undi_loader->PXENVptr.segment = rm_cs;
+ undi_loader->PXENVptr.offset = __from_text16 ( &pxenv );
+
+ undi_loader->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c
new file mode 100644
index 0000000..3939c7b
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c
@@ -0,0 +1,357 @@
+/** @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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/fakedhcp.h>
+#include <gpxe/device.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/isapnp.h>
+#include <gpxe/init.h>
+#include <gpxe/if_ether.h>
+#include <basemem_packet.h>
+#include <biosint.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/* Avoid dragging in isapnp.o unnecessarily */
+uint16_t isapnp_read_port;
+
+/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
+enum pxe_cached_info_indices {
+ CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ),
+ CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ),
+ CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ),
+ NUM_CACHED_INFOS
+};
+
+/** A cached DHCP packet */
+union pxe_cached_info {
+ struct dhcphdr dhcphdr;
+ /* This buffer must be *exactly* the size of a BOOTPLAYER_t
+ * structure, otherwise WinPE will die horribly. It takes the
+ * size of *our* buffer and feeds it in to us as the size of
+ * one of *its* buffers. If our buffer is larger than it
+ * expects, we therefore end up overwriting part of its data
+ * segment, since it tells us to do so. (D'oh!)
+ *
+ * Note that a BOOTPLAYER_t is not necessarily large enough to
+ * hold a DHCP packet; this is a flaw in the PXE spec.
+ */
+ BOOTPLAYER_t packet;
+} __attribute__ (( packed ));
+
+/** A PXE DHCP packet creator */
+struct pxe_dhcp_packet_creator {
+ /** Create DHCP packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ */
+ int ( * create ) ( struct net_device *netdev, void *data,
+ size_t max_len );
+};
+
+/** PXE DHCP packet creators */
+static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = {
+ [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover },
+ [CACHED_INFO_DHCPACK] = { create_fakedhcpack },
+ [CACHED_INFO_BINL] = { create_fakepxebsack },
+};
+
+/* The case in which the caller doesn't supply a buffer is really
+ * awkward to support given that we have multiple sources of options,
+ * and that we don't actually store the DHCP packets. (We may not
+ * even have performed DHCP; we may have obtained all configuration
+ * from non-volatile stored options or from the command line.)
+ *
+ * Some NBPs rely on the buffers we provide being persistent, so we
+ * can't just use the temporary packet buffer. 4.5kB of base memory
+ * always wasted just because some clients are too lazy to provide
+ * their own buffers...
+ */
+static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] );
+#define cached_info __use_data16 ( cached_info )
+
+/**
+ * Set PXE cached TFTP filename
+ *
+ * @v filename TFTP filename
+ *
+ * This is a bug-for-bug compatibility hack needed in order to work
+ * with Microsoft Remote Installation Services (RIS). The filename
+ * used in a call to PXENV_RESTART_TFTP or PXENV_TFTP_READ_FILE must
+ * be returned as the DHCP filename in subsequent calls to
+ * PXENV_GET_CACHED_INFO.
+ */
+void pxe_set_cached_filename ( const unsigned char *filename ) {
+ memcpy ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file, filename,
+ sizeof ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file ) );
+ memcpy ( cached_info[CACHED_INFO_BINL].dhcphdr.file, filename,
+ sizeof ( cached_info[CACHED_INFO_BINL].dhcphdr.file ) );
+}
+
+/**
+ * UNLOAD BASE CODE STACK
+ *
+ * @v None -
+ * @ret ...
+ *
+ */
+PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
+ DBG ( "PXENV_UNLOAD_STACK" );
+
+ 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 ) {
+ struct pxe_dhcp_packet_creator *creator;
+ union pxe_cached_info *info;
+ unsigned int idx;
+ size_t len;
+ userptr_t buffer;
+ int rc;
+
+ DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+
+ DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset, get_cached_info->BufferSize );
+
+ /* Sanity check */
+ idx = ( get_cached_info->PacketType - 1 );
+ if ( idx >= NUM_CACHED_INFOS ) {
+ DBG ( " bad PacketType" );
+ goto err;
+ }
+ info = &cached_info[idx];
+
+ /* Construct cached version of packet, if not already constructed. */
+ if ( ! info->dhcphdr.op ) {
+ /* Construct DHCP packet */
+ creator = &pxe_dhcp_packet_creators[idx];
+ if ( ( rc = creator->create ( pxe_netdev, info,
+ sizeof ( *info ) ) ) != 0 ) {
+ DBG ( " failed to build packet" );
+ goto err;
+ }
+ }
+
+ len = get_cached_info->BufferSize;
+ if ( len == 0 ) {
+ /* Point client at our cached buffer.
+ *
+ * To add to the fun, Intel decided at some point in
+ * the evolution of the PXE specification to add the
+ * BufferLimit field, which we are meant to fill in
+ * with the length of our packet buffer, so that the
+ * caller can safely modify the boot server reply
+ * packet stored therein. However, this field was not
+ * present in earlier versions of the PXE spec, and
+ * there is at least one PXE NBP (Altiris) which
+ * allocates only exactly enough space for this
+ * earlier, shorter version of the structure. If we
+ * actually fill in the BufferLimit field, we
+ * therefore risk trashing random areas of the
+ * caller's memory. If we *don't* fill it in, then
+ * the caller is at liberty to assume that whatever
+ * random value happened to be in that location
+ * represents the length of the buffer we've just
+ * passed back to it.
+ *
+ * Since older PXE stacks won't fill this field in
+ * anyway, it's probably safe to assume that no
+ * callers actually rely on it, so we choose to not
+ * fill it in.
+ */
+ get_cached_info->Buffer.segment = rm_ds;
+ get_cached_info->Buffer.offset = __from_data16 ( info );
+ get_cached_info->BufferSize = sizeof ( *info );
+ DBG ( " returning %04x:%04x+%04x['%x']",
+ get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset,
+ get_cached_info->BufferSize,
+ get_cached_info->BufferLimit );
+ } else {
+ /* Copy packet to client buffer */
+ if ( len > sizeof ( *info ) )
+ len = sizeof ( *info );
+ if ( len < sizeof ( *info ) )
+ DBG ( " buffer may be too short" );
+ buffer = real_to_user ( get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset );
+ copy_to_user ( buffer, 0, info, len );
+ get_cached_info->BufferSize = len;
+ }
+
+ get_cached_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ err:
+ get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* 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 " );
+
+ /* Intel bug-for-bug hack */
+ pxe_set_cached_filename ( restart_tftp->FileName );
+
+ /* Words cannot describe the complete mismatch between the PXE
+ * specification and any possible version of reality...
+ */
+ restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
+ restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
+ tftp_exit = pxenv_tftp_read_file ( restart_tftp );
+ if ( tftp_exit != PXENV_EXIT_SUCCESS )
+ return tftp_exit;
+
+ /* Fire up the new NBP */
+ restart_tftp->Status = pxe_start_nbp();
+
+ /* 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 int bus_type;
+ unsigned int location;
+ struct net_device *netdev;
+
+ DBG ( "PXENV_START_UNDI %04x:%04x:%04x",
+ start_undi->AX, start_undi->BX, start_undi->DX );
+
+ /* Determine bus type and location. Use a heuristic to decide
+ * whether we are PCI or ISAPnP
+ */
+ if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
+ ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
+ ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
+ ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
+ bus_type = BUS_TYPE_ISAPNP;
+ location = start_undi->BX;
+ /* Record ISAPnP read port for use by isapnp.c */
+ isapnp_read_port = start_undi->DX;
+ } else {
+ bus_type = BUS_TYPE_PCI;
+ location = start_undi->AX;
+ }
+
+ /* Probe for devices, etc. */
+ startup();
+
+ /* Look for a matching net device */
+ netdev = find_netdev_by_location ( bus_type, location );
+ if ( ! netdev ) {
+ DBG ( " no net device found" );
+ start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBG ( " using netdev %s", netdev->name );
+
+ /* Activate PXE */
+ pxe_activate ( netdev );
+
+ 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" );
+
+ /* Deactivate PXE */
+ pxe_deactivate();
+
+ /* Prepare for unload */
+ shutdown ( SHUTDOWN_BOOT );
+
+ /* Check to see if we still have any hooked interrupts */
+ if ( hooked_bios_interrupts != 0 ) {
+ DBG ( "PXENV_STOP_UNDI failed: %d interrupts still hooked\n",
+ hooked_bios_interrupts );
+ 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" );
+
+ start_base->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_STOP_BASE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
+ DBG ( "PXENV_STOP_BASE" );
+
+ /* The only time we will be called is when the NBP is trying
+ * to shut down the PXE stack. There's nothing we need to do
+ * in this call.
+ */
+
+ stop_base->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c
new file mode 100644
index 0000000..0e3ca3c
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c
@@ -0,0 +1,586 @@
+/** @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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/in.h>
+#include <gpxe/tftp.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/process.h>
+#include <pxe.h>
+
+/** A PXE TFTP connection */
+struct pxe_tftp_connection {
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Data buffer */
+ userptr_t buffer;
+ /** Size of data buffer */
+ size_t size;
+ /** Starting offset of data buffer */
+ size_t start;
+ /** File position */
+ size_t offset;
+ /** Maximum file position */
+ size_t max_offset;
+ /** Block size */
+ size_t blksize;
+ /** Block index */
+ unsigned int blkidx;
+ /** Overall return status code */
+ int rc;
+};
+
+/** The PXE TFTP connection */
+static struct pxe_tftp_connection pxe_tftp = {
+ .xfer = XFER_INIT ( &null_xfer_ops ),
+};
+
+/**
+ * Close PXE TFTP connection
+ *
+ * @v rc Final status code
+ */
+static void pxe_tftp_close ( int rc ) {
+ xfer_nullify ( &pxe_tftp.xfer );
+ xfer_close ( &pxe_tftp.xfer, rc );
+ pxe_tftp.rc = rc;
+}
+
+/**
+ * Receive new data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata
+ * @ret rc Return status code
+ */
+static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ size_t len = iob_len ( iobuf );
+ int rc = 0;
+
+ /* Calculate new buffer position */
+ if ( meta->whence != SEEK_CUR )
+ pxe_tftp.offset = 0;
+ pxe_tftp.offset += meta->offset;
+
+ /* Copy data block to buffer */
+ if ( len == 0 ) {
+ /* No data (pure seek); treat as success */
+ } else if ( pxe_tftp.offset < pxe_tftp.start ) {
+ DBG ( " buffer underrun at %zx (min %zx)",
+ pxe_tftp.offset, pxe_tftp.start );
+ rc = -ENOBUFS;
+ } else if ( ( pxe_tftp.offset + len ) >
+ ( pxe_tftp.start + pxe_tftp.size ) ) {
+ DBG ( " buffer overrun at %zx (max %zx)",
+ ( pxe_tftp.offset + len ),
+ ( pxe_tftp.start + pxe_tftp.size ) );
+ rc = -ENOBUFS;
+ } else {
+ copy_to_user ( pxe_tftp.buffer,
+ ( pxe_tftp.offset - pxe_tftp.start ),
+ iobuf->data, len );
+ }
+
+ /* Calculate new buffer position */
+ pxe_tftp.offset += len;
+
+ /* Record maximum offset as the file size */
+ if ( pxe_tftp.max_offset < pxe_tftp.offset )
+ pxe_tftp.max_offset = pxe_tftp.offset;
+
+ /* Terminate transfer on error */
+ if ( rc != 0 )
+ pxe_tftp_close ( rc );
+
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle close() event
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
+ int rc ) {
+ pxe_tftp_close ( rc );
+}
+
+static struct xfer_interface_operations pxe_tftp_xfer_ops = {
+ .close = pxe_tftp_xfer_close,
+ .vredirect = xfer_vreopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = pxe_tftp_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Maximum length of a PXE TFTP URI
+ *
+ * The PXE TFTP API provides 128 characters for the filename; the
+ * extra 128 bytes allow for the remainder of the URI.
+ */
+#define PXE_TFTP_URI_LEN 256
+
+/**
+ * Open PXE TFTP connection
+ *
+ * @v ipaddress IP address
+ * @v port TFTP server port
+ * @v filename File name
+ * @v blksize Requested block size
+ * @ret rc Return status code
+ */
+static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
+ const unsigned char *filename, size_t blksize,
+ int sizeonly ) {
+ char uri_string[PXE_TFTP_URI_LEN];
+ struct in_addr address;
+ int rc;
+
+ /* Intel bug-for-bug hack */
+ pxe_set_cached_filename ( filename );
+
+ /* Reset PXE TFTP connection structure */
+ memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
+ xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL );
+ pxe_tftp.rc = -EINPROGRESS;
+
+ /* Construct URI string */
+ address.s_addr = ipaddress;
+ if ( ! port )
+ port = htons ( TFTP_PORT );
+ if ( blksize < TFTP_DEFAULT_BLKSIZE )
+ blksize = TFTP_DEFAULT_BLKSIZE;
+ snprintf ( uri_string, sizeof ( uri_string ),
+ "tftp%s://%s:%d%s%s?blksize=%zd",
+ sizeonly ? "size" : "",
+ inet_ntoa ( address ), ntohs ( port ),
+ ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
+ DBG ( " %s", uri_string );
+
+ /* Open PXE TFTP connection */
+ if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer,
+ uri_string ) ) != 0 ) {
+ DBG ( " could not open (%s)\n", strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TFTP OPEN
+ *
+ * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN
+ * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
+ * @v s_PXENV_TFTP_OPEN::FileName Name of file to open
+ * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port
+ * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_TFTP_OPEN::Status PXE status code
+ * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated blksize
+ * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small
+ *
+ * Opens a TFTP connection for downloading a file a block at a time
+ * using pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * 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 According to the PXE specification version 2.1, this call
+ * "opens a file for reading/writing", though how writing is to be
+ * achieved without the existence of an API call %pxenv_tftp_write()
+ * is not made clear.
+ *
+ * @note Despite the existence of the numerous statements within the
+ * PXE specification of the form "...if a TFTP/MTFTP or UDP connection
+ * is active...", you cannot use pxenv_tftp_open() and
+ * pxenv_tftp_read() to read a file via MTFTP; only via plain old
+ * TFTP. If you want to use MTFTP, use pxenv_tftp_read_file()
+ * instead. Astute readers will note that, since
+ * pxenv_tftp_read_file() is an atomic operation from the point of
+ * view of the PXE API, it is conceptually impossible to issue any
+ * other PXE API call "if an MTFTP connection is active".
+ */
+PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_OPEN" );
+
+ /* Guard against callers that fail to close before re-opening */
+ pxe_tftp_close ( 0 );
+
+ /* Open connection */
+ if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
+ tftp_open->TFTPPort,
+ tftp_open->FileName,
+ tftp_open->PacketSize,
+ 0) ) != 0 ) {
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for OACK to arrive so that we have the block size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.max_offset == 0 ) ) {
+ step();
+ }
+ pxe_tftp.blksize = xfer_window ( &pxe_tftp.xfer );
+ tftp_open->PacketSize = pxe_tftp.blksize;
+ DBG ( " blksize=%d", tftp_open->PacketSize );
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP CLOSE
+ *
+ * @v tftp_close Pointer to a struct s_PXENV_TFTP_CLOSE
+ * @ret #PXENV_EXIT_SUCCESS File was closed successfully
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_TFTP_CLOSE::Status PXE status code
+ * @err None -
+ *
+ * Close a connection previously opened with pxenv_tftp_open(). You
+ * must have previously opened a connection with pxenv_tftp_open().
+ *
+ * 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.)
+ */
+PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
+ DBG ( "PXENV_TFTP_CLOSE" );
+
+ pxe_tftp_close ( 0 );
+ tftp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * TFTP READ
+ *
+ * @v tftp_read Pointer to a struct s_PXENV_TFTP_READ
+ * @v s_PXENV_TFTP_READ::Buffer Address of data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data was read successfully
+ * @ret #PXENV_EXIT_FAILURE Data was not read
+ * @ret s_PXENV_TFTP_READ::Status PXE status code
+ * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number
+ * @ret s_PXENV_TFTP_READ::BufferSize Length of data written into buffer
+ *
+ * Reads a single packet from a connection previously opened with
+ * pxenv_tftp_open() into the data buffer pointed to by
+ * s_PXENV_TFTP_READ::Buffer. You must have previously opened a
+ * connection with pxenv_tftp_open(). The data written into
+ * s_PXENV_TFTP_READ::Buffer is just the file data; the various
+ * network headers have already been removed.
+ *
+ * The buffer must be large enough to contain a packet of the size
+ * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the
+ * pxenv_tftp_open() call. It is worth noting that the PXE
+ * specification does @b not require the caller to fill in
+ * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so
+ * the PXE stack is free to ignore whatever value the caller might
+ * place there and just assume that the buffer is large enough. That
+ * said, it may be worth the caller always filling in
+ * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that
+ * mistake it for an input parameter.
+ *
+ * The length of the TFTP data packet will be returned via
+ * s_PXENV_TFTP_READ::BufferSize. If this length is less than the
+ * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to
+ * pxenv_tftp_open(), this indicates that the block is the last block
+ * in the file. Note that zero is a valid length for
+ * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of
+ * the file is a multiple of the blksize.
+ *
+ * The PXE specification doesn't actually state that calls to
+ * pxenv_tftp_read() will return the data packets in strict sequential
+ * order, though most PXE stacks will probably do so. The sequence
+ * number of the packet will be returned in
+ * s_PXENV_TFTP_READ::PacketNumber. The first packet in the file has
+ * a sequence number of one, not zero.
+ *
+ * To guard against flawed PXE stacks, the caller should probably set
+ * s_PXENV_TFTP_READ::PacketNumber to one less than the expected
+ * returned value (i.e. set it to zero for the first call to
+ * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ
+ * parameter block for subsequent calls without modifying
+ * s_PXENV_TFTP_READ::PacketNumber between calls). The caller should
+ * also guard against potential problems caused by flawed
+ * implementations returning the occasional duplicate packet, by
+ * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber
+ * is as expected (i.e. one greater than that returned from the
+ * previous call to pxenv_tftp_read()).
+ *
+ * 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.)
+ */
+PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ to %04x:%04x",
+ tftp_read->Buffer.segment, tftp_read->Buffer.offset );
+
+ /* Read single block into buffer */
+ pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
+ tftp_read->Buffer.offset );
+ pxe_tftp.size = pxe_tftp.blksize;
+ pxe_tftp.start = pxe_tftp.offset;
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.offset == pxe_tftp.start ) )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
+ tftp_read->PacketNumber = ++pxe_tftp.blkidx;
+
+ /* EINPROGRESS is normal if we haven't reached EOF yet */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_read->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP/MTFTP read file
+ *
+ * @v tftp_read_file Pointer to a struct s_PXENV_TFTP_READ_FILE
+ * @v s_PXENV_TFTP_READ_FILE::FileName File name
+ * @v s_PXENV_TFTP_READ_FILE::BufferSize Size of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::Buffer Address of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_READ_FILE::McastIPAddress File's multicast IP address
+ * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort Client multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort Server multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut Time to wait for first packet
+ * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay MTFTP inactivity timeout
+ * @ret #PXENV_EXIT_SUCCESS File downloaded successfully
+ * @ret #PXENV_EXIT_FAILURE File not downloaded
+ * @ret s_PXENV_TFTP_READ_FILE::Status PXE status code
+ * @ret s_PXENV_TFTP_READ_FILE::BufferSize Length of downloaded file
+ *
+ * Downloads an entire file via either TFTP or MTFTP into the buffer
+ * pointed to by s_PXENV_TFTP_READ_FILE::Buffer.
+ *
+ * The PXE specification does not make it clear how the caller
+ * requests that MTFTP be used rather than TFTP (or vice versa). One
+ * reasonable guess is that setting
+ * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP
+ * to be used instead of MTFTP, though it is conceivable that some PXE
+ * stacks would interpret that as "use the DHCP-provided multicast IP
+ * address" instead. Some PXE stacks will not implement MTFTP at all,
+ * and will always use TFTP.
+ *
+ * It is not specified whether or not
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server
+ * port for TFTP (rather than MTFTP) downloads. Callers should assume
+ * that the only way to access a TFTP server on a non-standard port is
+ * to use pxenv_tftp_open() and pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an
+ * #ADDR32_t type, i.e. nominally a flat physical address. Some PXE
+ * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real
+ * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above
+ * 1MB. This means that PXE stacks must be prepared to write to areas
+ * outside base memory. Exactly how this is to be achieved is not
+ * specified, though using INT 15,87 is as close to a standard method
+ * as any, and should probably be used. Switching to protected-mode
+ * in order to access high memory will fail if pxenv_tftp_read_file()
+ * is called in V86 mode; it is reasonably to expect that a V86
+ * monitor would intercept the relatively well-defined INT 15,87 if it
+ * wants the PXE stack to be able to write to high memory.
+ *
+ * Things get even more interesting if pxenv_tftp_read_file() is
+ * called in protected mode, because there is then absolutely no way
+ * for the PXE stack to write to an absolute physical address. You
+ * can't even get around the problem by creating a special "access
+ * everything" segment in the s_PXE data structure, because the
+ * #SEGDESC_t descriptors are limited to 64kB in size.
+ *
+ * Previous versions of the PXE specification (e.g. WfM 1.1a) provide
+ * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to
+ * work around this problem. The s_PXENV_TFTP_READ_FILE_PMODE
+ * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a
+ * protected-mode segment:offset address for the data buffer. This
+ * API call is no longer present in version 2.1 of the PXE
+ * specification.
+ *
+ * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer
+ * is an offset relative to the caller's data segment, when
+ * pxenv_tftp_read_file() is called in protected mode.
+ *
+ * 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 Microsoft's NTLDR assumes that the filename passed in via
+ * s_PXENV_TFTP_READ_FILE::FileName will be stored in the "file" field
+ * of the stored DHCPACK packet, whence it will be returned via any
+ * subsequent calls to pxenv_get_cached_info(). Though this is
+ * essentially a bug in the Intel PXE implementation (not, for once,
+ * in the specification!), it is a bug that Microsoft relies upon, and
+ * so we implement this bug-for-bug compatibility by overwriting the
+ * filename stored DHCPACK packet with the filename passed in
+ * s_PXENV_TFTP_READ_FILE::FileName.
+ *
+ */
+PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ_FILE to %08x+%x", tftp_read_file->Buffer,
+ tftp_read_file->BufferSize );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
+ tftp_read_file->FileName, 0, 0 ) ) != 0 ) {
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Read entire file */
+ pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
+ pxe_tftp.size = tftp_read_file->BufferSize;
+ while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read_file->BufferSize = pxe_tftp.max_offset;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( rc );
+
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP GET FILE SIZE
+ *
+ * @v tftp_get_fsize Pointer to a struct s_PXENV_TFTP_GET_FSIZE
+ * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::FileName File name
+ * @ret #PXENV_EXIT_SUCCESS File size was determined successfully
+ * @ret #PXENV_EXIT_FAILURE File size was not determined
+ * @ret s_PXENV_TFTP_GET_FSIZE::Status PXE status code
+ * @ret s_PXENV_TFTP_GET_FSIZE::FileSize File size
+ *
+ * Determine the size of a file on a TFTP server. This uses the
+ * "tsize" TFTP option, and so will not work with a TFTP server that
+ * does not support TFTP options, or that does not support the "tsize"
+ * option.
+ *
+ * The PXE specification states that this API call will @b not open a
+ * TFTP connection for subsequent use with pxenv_tftp_read(). (This
+ * is somewhat daft, since the only way to obtain the file size via
+ * the "tsize" option involves issuing a TFTP open request, but that's
+ * life.)
+ *
+ * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP
+ * connection is open.
+ *
+ * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * 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 There is no way to specify the TFTP server port with this API
+ * call. Though you can open a file using a non-standard TFTP server
+ * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially,
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of
+ * a file from a TFTP server listening on the standard TFTP port.
+ * "Consistency" is not a word in Intel's vocabulary.
+ */
+PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+ *tftp_get_fsize ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_GET_FSIZE" );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
+ tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) {
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for initial seek to arrive, and record size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.max_offset == 0 ) ) {
+ step();
+ }
+ tftp_get_fsize->FileSize = pxe_tftp.max_offset;
+ DBG ( " fsize=%d", tftp_get_fsize->FileSize );
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( rc );
+
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_udp.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_udp.c
new file mode 100644
index 0000000..f470220
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_udp.c
@@ -0,0 +1,405 @@
+/** @file
+ *
+ * PXE UDP API
+ *
+ */
+
+#include <string.h>
+#include <byteswap.h>
+#include <gpxe/xfer.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>.
+ *
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** A PXE UDP connection */
+struct pxe_udp_connection {
+ /** Data transfer interface to UDP stack */
+ struct xfer_interface xfer;
+ /** Local address */
+ struct sockaddr_in local;
+ /** Current PXENV_UDP_READ parameter block */
+ struct s_PXENV_UDP_READ *pxenv_udp_read;
+};
+
+/**
+ * Receive PXE UDP data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ *
+ * Receives a packet as part of the current pxenv_udp_read()
+ * operation.
+ */
+static int pxe_udp_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct pxe_udp_connection *pxe_udp =
+ container_of ( xfer, struct pxe_udp_connection, xfer );
+ struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
+ struct sockaddr_in *sin_src;
+ struct sockaddr_in *sin_dest;
+ userptr_t buffer;
+ size_t len;
+ int rc = 0;
+
+ if ( ! pxenv_udp_read ) {
+ DBG ( "PXE discarded UDP packet\n" );
+ rc = -ENOBUFS;
+ goto done;
+ }
+
+ /* Copy packet to buffer and record length */
+ buffer = real_to_user ( pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset );
+ len = iob_len ( iobuf );
+ if ( len > pxenv_udp_read->buffer_size )
+ len = pxenv_udp_read->buffer_size;
+ copy_to_user ( buffer, 0, iobuf->data, len );
+ pxenv_udp_read->buffer_size = len;
+
+ /* Fill in source/dest information */
+ assert ( meta );
+ sin_src = ( struct sockaddr_in * ) meta->src;
+ assert ( sin_src );
+ 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;
+ sin_dest = ( struct sockaddr_in * ) meta->dest;
+ assert ( sin_dest );
+ 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;
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** PXE UDP data transfer interface operations */
+static struct xfer_interface_operations pxe_udp_xfer_operations = {
+ .close = ignore_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = pxe_udp_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/** The PXE UDP connection */
+static struct pxe_udp_connection pxe_udp = {
+ .xfer = XFER_INIT ( &pxe_udp_xfer_operations ),
+ .local = {
+ .sin_family = AF_INET,
+ },
+};
+
+/**
+ * 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_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().
+ *
+ * The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be
+ * recorded and used as the local station's IP address for all further
+ * communication, including communication by means other than
+ * pxenv_udp_write() and pxenv_udp_read(). (If
+ * 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. 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
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note The PXE specification does not make it clear whether the IP
+ * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only
+ * 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 *pxenv_udp_open ) {
+ int rc;
+
+ DBG ( "PXENV_UDP_OPEN" );
+
+ /* Record source IP address */
+ pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
+ DBG ( " %s", inet_ntoa ( pxe_udp.local.sin_addr ) );
+
+ /* Open promiscuous UDP connection */
+ xfer_close ( &pxe_udp.xfer, 0 );
+ if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
+ pxenv_udp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * 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().
+ *
+ * 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.
+ *
+ * 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.)
+ *
+ */
+PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
+ DBG ( "PXENV_UDP_CLOSE" );
+
+ /* Close UDP connection */
+ xfer_close ( &pxe_udp.xfer, 0 );
+
+ pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * 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
+ * @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 transmitted
+ * @ret s_PXENV_UDP_WRITE::Status PXE status code
+ * @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
+ * should not contain precomputed IP and UDP headers, nor should it
+ * contain space allocated for these headers. The first byte of the
+ * buffer will be transmitted as the first byte following the UDP
+ * header.
+ *
+ * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
+ * place. See the relevant @ref pxe_routing "implementation note" for
+ * more details.
+ *
+ * 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().
+ *
+ * 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 *pxenv_udp_write ) {
+ struct sockaddr_in dest;
+ struct xfer_metadata meta = {
+ .src = ( struct sockaddr * ) &pxe_udp.local,
+ .dest = ( struct sockaddr * ) &dest,
+ .netdev = pxe_netdev,
+ };
+ size_t len;
+ struct io_buffer *iobuf;
+ userptr_t buffer;
+ int rc;
+
+ DBG ( "PXENV_UDP_WRITE" );
+
+ /* Construct destination socket address */
+ memset ( &dest, 0, sizeof ( dest ) );
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = pxenv_udp_write->ip;
+ dest.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.
+ */
+ pxe_udp.local.sin_port = pxenv_udp_write->src_port;
+ if ( ! pxe_udp.local.sin_port )
+ pxe_udp.local.sin_port = htons ( 2069 );
+
+ /* 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.
+ */
+
+ /* Allocate and fill data buffer */
+ len = pxenv_udp_write->buffer_size;
+ iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
+ if ( ! iobuf ) {
+ pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ buffer = real_to_user ( pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset );
+ copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );
+
+ DBG ( " %04x:%04x+%x %d->%s:%d", pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
+ ntohs ( pxenv_udp_write->src_port ),
+ inet_ntoa ( dest.sin_addr ),
+ ntohs ( pxenv_udp_write->dst_port ) );
+
+ /* Transmit packet */
+ if ( ( rc = xfer_deliver_iob_meta ( &pxe_udp.xfer, iobuf,
+ &meta ) ) != 0 ) {
+ pxenv_udp_write->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * 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
+ * @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 s_PXENV_UDP_READ::Status PXE status code
+ * @ret s_PXENV_UDP_READ::src_ip Source 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_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
+ * packet is ready to read, the call will return instantly with
+ * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
+ *
+ * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
+ * any IP address will be accepted and may be returned to the caller.
+ *
+ * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
+ * 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().
+ *
+ * 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 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
+ * expects us to do so, and will fail if we don't.
+ *
+ */
+PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
+ struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
+ struct in_addr dest_ip;
+ uint16_t d_port_wanted = pxenv_udp_read->d_port;
+ uint16_t d_port;
+
+ DBG ( "PXENV_UDP_READ" );
+
+ /* 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;
+ }
+ dest_ip.s_addr = pxenv_udp_read->dest_ip;
+ d_port = pxenv_udp_read->d_port;
+
+ /* Filter on destination address and/or port */
+ if ( dest_ip_wanted.s_addr &&
+ ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
+ DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
+ DBG ( " (wanted %s)", inet_ntoa ( dest_ip_wanted ) );
+ goto no_packet;
+ }
+ if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
+ DBG ( " wrong port %d ", htons ( d_port ) );
+ DBG ( " (wanted %d)", htons ( d_port_wanted ) );
+ goto no_packet;
+ }
+
+ DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
+ DBG ( "%d<-%s:%d", ntohs ( pxenv_udp_read->s_port ),
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
+ ntohs ( pxenv_udp_read->d_port ) );
+
+ pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ no_packet:
+ pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+}
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_undi.c b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_undi.c
new file mode 100644
index 0000000..c9b67c0
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/interface/pxe/pxe_undi.c
@@ -0,0 +1,791 @@
+/** @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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+#include <basemem_packet.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/device.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ip.h>
+#include <gpxe/arp.h>
+#include <gpxe/rarp.h>
+#include "pxe.h"
+
+/**
+ * Count of outstanding transmitted packets
+ *
+ * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
+ * decremented each time that PXENV_UNDI_ISR is called with the TX
+ * queue empty, stopping when the count reaches zero. This allows us
+ * to provide a pessimistic approximation of TX completion events to
+ * the PXE NBP simply by monitoring the netdev's TX queue.
+ */
+static int undi_tx_count = 0;
+
+struct net_device *pxe_netdev = NULL;
+
+/**
+ * Set network device as current PXE network device
+ *
+ * @v netdev Network device, or NULL
+ */
+void pxe_set_netdev ( struct net_device *netdev ) {
+ if ( pxe_netdev )
+ netdev_put ( pxe_netdev );
+ pxe_netdev = NULL;
+ if ( netdev )
+ pxe_netdev = netdev_get ( netdev );
+}
+
+/**
+ * Open PXE network device
+ *
+ * @ret rc Return status code
+ */
+static int pxe_netdev_open ( void ) {
+ int rc;
+
+ if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
+ return rc;
+
+ netdev_irq ( pxe_netdev, 1 );
+ return 0;
+}
+
+/**
+ * Close PXE network device
+ *
+ */
+static void pxe_netdev_close ( void ) {
+ netdev_irq ( pxe_netdev, 0 );
+ netdev_close ( pxe_netdev );
+ undi_tx_count = 0;
+}
+
+/**
+ * Dump multicast address list
+ *
+ * @v mcast PXE multicast address list
+ */
+static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+ unsigned int i;
+
+ for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
+ DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
+ }
+}
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
+ DBG ( "PXENV_UNDI_STARTUP\n" );
+
+ 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\n" );
+
+ pxe_netdev_close();
+
+ 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 protocolini %08x\n",
+ undi_initialize->ProtocolIni );
+
+ 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 ) {
+ int rc;
+
+ DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+ pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
+ DBG ( "\n" );
+
+ pxe_netdev_close();
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
+ pxe_netdev->name, strerror ( rc ) );
+ undi_reset_adapter->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ 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\n" );
+
+ pxe_netdev_close();
+
+ 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 ) {
+ int rc;
+
+ DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
+ undi_open->OpenFlag, undi_open->PktFilter );
+ pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
+ DBG ( "\n" );
+
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
+ pxe_netdev->name, strerror ( rc ) );
+ undi_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ 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\n" );
+
+ pxe_netdev_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;
+ struct DataBlk *datablk;
+ struct io_buffer *iobuf;
+ struct net_protocol *net_protocol;
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+ char destaddr[MAX_LL_ADDR_LEN];
+ const void *ll_dest;
+ size_t ll_hlen = ll_protocol->ll_header_len;
+ size_t len;
+ unsigned int i;
+ int rc;
+
+ DBG2 ( "PXENV_UNDI_TRANSMIT" );
+
+ /* Forcibly enable interrupts at this point, to work around
+ * callers that never call PXENV_UNDI_OPEN before attempting
+ * to use the UNDI API.
+ */
+ netdev_irq ( pxe_netdev, 1 );
+
+ /* Identify network-layer protocol */
+ switch ( undi_transmit->Protocol ) {
+ case P_IP: net_protocol = &ipv4_protocol; break;
+ case P_ARP: net_protocol = &arp_protocol; break;
+ case P_RARP: net_protocol = &rarp_protocol; break;
+ case P_UNKNOWN:
+ net_protocol = NULL;
+ ll_hlen = 0;
+ break;
+ default:
+ DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
+
+ /* Calculate total packet length */
+ copy_from_real ( &tbd, undi_transmit->TBD.segment,
+ undi_transmit->TBD.offset, sizeof ( tbd ) );
+ len = tbd.ImmedLength;
+ DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
+ tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ len += datablk->TDDataLen;
+ DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
+ datablk->TDDataPtr.offset, datablk->TDDataLen );
+ }
+
+ /* Allocate and fill I/O buffer */
+ iobuf = alloc_iob ( ll_hlen + len );
+ if ( ! iobuf ) {
+ DBG2 ( " could not allocate iobuf\n" );
+ undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ iob_reserve ( iobuf, ll_hlen );
+ copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
+ tbd.Xmit.offset, tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
+ datablk->TDDataPtr.segment,
+ datablk->TDDataPtr.offset,
+ datablk->TDDataLen );
+ }
+
+ /* Add link-layer header, if required to do so */
+ if ( net_protocol != NULL ) {
+
+ /* Calculate destination address */
+ if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
+ copy_from_real ( destaddr,
+ undi_transmit->DestAddr.segment,
+ undi_transmit->DestAddr.offset,
+ ll_protocol->ll_addr_len );
+ ll_dest = destaddr;
+ DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
+ } else {
+ ll_dest = pxe_netdev->ll_broadcast;
+ DBG2 ( " BCAST" );
+ }
+
+ /* Add link-layer header */
+ if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
+ pxe_netdev->ll_addr,
+ net_protocol->net_proto ))!=0){
+ DBG2 ( " could not add link-layer header: %s\n",
+ strerror ( rc ) );
+ free_iob ( iobuf );
+ undi_transmit->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ /* Flag transmission as in-progress. Do this before starting
+ * to transmit the packet, because the ISR may trigger before
+ * we return from netdev_tx().
+ */
+ undi_tx_count++;
+
+ /* Transmit packet */
+ DBG2 ( "\n" );
+ if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
+ DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
+ strerror ( rc ) );
+ undi_tx_count--;
+ undi_transmit->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_transmit->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * Status: working (for NICs that support receive-all-multicast)
+ */
+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" );
+ pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
+ DBG ( "\n" );
+
+ undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t
+pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
+ *undi_set_station_address ) {
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
+ ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
+
+ /* If adapter is open, the change will have no effect; return
+ * an error
+ */
+ if ( pxe_netdev->state & NETDEV_OPEN ) {
+ DBG ( " failed: netdev is open\n" );
+ undi_set_station_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Update MAC address */
+ memcpy ( pxe_netdev->ll_addr,
+ &undi_set_station_address->StationAddress,
+ ll_protocol->ll_addr_len );
+
+ DBG ( "\n" );
+ undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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 %02x\n",
+ undi_set_packet_filter->filter );
+
+ /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
+ * driver refuses to load. (We ignore the filter value in the
+ * PXENV_UNDI_OPEN call anyway.)
+ */
+ undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
+
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_INFORMATION
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
+ *undi_get_information ) {
+ struct device *dev = pxe_netdev->dev;
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+ size_t ll_addr_len = ll_protocol->ll_addr_len;
+
+ DBG ( "PXENV_UNDI_GET_INFORMATION" );
+
+ undi_get_information->BaseIo = dev->desc.ioaddr;
+ undi_get_information->IntNumber = dev->desc.irq;
+ /* Cheat: assume all cards can cope with this */
+ undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+ undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
+ undi_get_information->HwAddrLen = ll_addr_len;
+ assert ( ll_addr_len <=
+ sizeof ( undi_get_information->CurrentNodeAddress ) );
+ memcpy ( &undi_get_information->CurrentNodeAddress,
+ pxe_netdev->ll_addr,
+ sizeof ( undi_get_information->CurrentNodeAddress ) );
+ ll_protocol->init_addr ( pxe_netdev->hw_addr,
+ &undi_get_information->PermNodeAddress );
+ 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;
+
+ DBG ( " io %04x irq %d mtu %d %s %s\n",
+ undi_get_information->BaseIo, undi_get_information->IntNumber,
+ undi_get_information->MaxTranUnit, ll_protocol->name,
+ ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
+ undi_get_information->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATISTICS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
+ *undi_get_statistics ) {
+ DBG ( "PXENV_UNDI_GET_STATISTICS" );
+
+ undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
+ undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
+ undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
+ undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
+
+ DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
+ undi_get_statistics->XmtGoodFrames,
+ undi_get_statistics->RcvGoodFrames,
+ undi_get_statistics->RcvCRCErrors,
+ undi_get_statistics->RcvResourceErrors );
+ undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
+ *undi_clear_statistics ) {
+ DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
+
+ memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
+ memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
+
+ undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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 failed: unsupported\n" );
+
+ undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: won't implement (would require driver API changes for no
+ * perceptible benefit)
+ */
+PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
+ *undi_force_interrupt ) {
+ DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
+
+ undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t
+pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
+ *undi_get_mcast_address ) {
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+ struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
+ int rc;
+
+ DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
+
+ if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
+ undi_get_mcast_address->MediaAddr ))!=0){
+ DBG ( " failed: %s\n", strerror ( rc ) );
+ undi_get_mcast_address->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ DBG ( "=>%s\n",
+ ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
+
+ undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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 ) {
+ struct device *dev = pxe_netdev->dev;
+
+ DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
+
+ memset ( &undi_get_nic_type->info, 0,
+ sizeof ( undi_get_nic_type->info ) );
+
+ switch ( dev->desc.bus_type ) {
+ case BUS_TYPE_PCI: {
+ struct pci_nic_info *info = &undi_get_nic_type->info.pci;
+
+ undi_get_nic_type->NicType = PCI_NIC;
+ info->Vendor_ID = dev->desc.vendor;
+ info->Dev_ID = dev->desc.device;
+ info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
+ info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
+ info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
+ info->BusDevFunc = dev->desc.location;
+ /* Cheat: remaining fields are probably unnecessary,
+ * and would require adding extra code to pci.c.
+ */
+ undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
+ undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+ DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
+ "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
+ PCI_SLOT ( info->BusDevFunc ),
+ PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
+ info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
+ info->Base_Class, info->Sub_Class, info->Prog_Intf,
+ info->Rev );
+ break; }
+ case BUS_TYPE_ISAPNP: {
+ struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
+
+ undi_get_nic_type->NicType = PnP_NIC;
+ info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
+ dev->desc.device );
+ info->CardSelNum = dev->desc.location;
+ /* Cheat: remaining fields are probably unnecessary,
+ * and would require adding extra code to isapnp.c.
+ */
+ DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
+ info->CardSelNum, info->EISA_Dev_ID,
+ info->Base_Class, info->Sub_Class, info->Prog_Intf );
+ break; }
+ default:
+ DBG ( " failed: unknown bus type\n" );
+ undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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" );
+
+ /* Just hand back some info, doesn't really matter what it is.
+ * Most PXE stacks seem to take this approach.
+ */
+ snprintf ( ( char * ) undi_get_iface_info->IfaceType,
+ sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
+ undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
+ undi_get_iface_info->ServiceFlags =
+ ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
+ SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
+ SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
+ memset ( undi_get_iface_info->Reserved, 0,
+ sizeof(undi_get_iface_info->Reserved) );
+
+ DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
+ undi_get_iface_info->LinkSpeed,
+ undi_get_iface_info->ServiceFlags );
+ 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 ) {
+ DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
+
+ 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 ) {
+ struct io_buffer *iobuf;
+ size_t len;
+ struct ll_protocol *ll_protocol;
+ const void *ll_dest;
+ const void *ll_source;
+ uint16_t net_proto;
+ size_t ll_hlen;
+ struct net_protocol *net_protocol;
+ unsigned int prottype;
+ int rc;
+
+ /* Use coloured debug, since UNDI ISR messages are likely to
+ * be interspersed amongst other UNDI messages.
+ */
+ DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
+
+ /* 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 :
+ DBGC2 ( &pxenv_undi_isr, " START" );
+
+ /* Call poll(). This should acknowledge the device
+ * interrupt and queue up any received packet.
+ */
+ netdev_poll ( pxe_netdev );
+
+ /* Disable interrupts to avoid interrupt storm */
+ netdev_irq ( pxe_netdev, 0 );
+
+ /* Always say it was ours for the sake of simplicity */
+ DBGC2 ( &pxenv_undi_isr, " OURS" );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+ break;
+ case PXENV_UNDI_ISR_IN_PROCESS :
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
+ DBGC2 ( &pxenv_undi_isr, " %s",
+ ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
+ "PROCESS" : "GET_NEXT" ) );
+
+ /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
+ * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
+ * they just sit in a tight polling loop merrily
+ * violating the PXE spec with repeated calls to
+ * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to
+ * cope with these out-of-spec clients.
+ */
+ netdev_poll ( pxe_netdev );
+
+ /* If we have not yet marked a TX as complete, and the
+ * netdev TX queue is empty, report the TX completion.
+ */
+ if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
+ DBGC2 ( &pxenv_undi_isr, " TXC" );
+ undi_tx_count--;
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
+ break;
+ }
+
+ /* Remove first packet from netdev RX queue */
+ iobuf = netdev_rx_dequeue ( pxe_netdev );
+ if ( ! iobuf ) {
+ DBGC2 ( &pxenv_undi_isr, " DONE" );
+ /* No more packets remaining */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ /* Re-enable interrupts */
+ netdev_irq ( pxe_netdev, 1 );
+ break;
+ }
+
+ /* Copy packet to base memory buffer */
+ len = iob_len ( iobuf );
+ DBGC2 ( &pxenv_undi_isr, " RX" );
+ if ( len > sizeof ( basemem_packet ) ) {
+ /* Should never happen */
+ DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
+ len = sizeof ( basemem_packet );
+ }
+ memcpy ( basemem_packet, iobuf->data, len );
+
+ /* Strip link-layer header */
+ ll_protocol = pxe_netdev->ll_protocol;
+ if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
+ &ll_source, &net_proto )) !=0){
+ /* Assume unknown net_proto and no ll_source */
+ net_proto = 0;
+ ll_source = NULL;
+ }
+ ll_hlen = ( len - iob_len ( iobuf ) );
+
+ /* Determine network-layer protocol */
+ switch ( net_proto ) {
+ case htons ( ETH_P_IP ):
+ net_protocol = &ipv4_protocol;
+ prottype = P_IP;
+ break;
+ case htons ( ETH_P_ARP ):
+ net_protocol = &arp_protocol;
+ prottype = P_ARP;
+ break;
+ case htons ( ETH_P_RARP ):
+ net_protocol = &rarp_protocol;
+ prottype = P_RARP;
+ break;
+ default:
+ net_protocol = NULL;
+ prottype = P_UNKNOWN;
+ break;
+ }
+
+ /* Fill in UNDI_ISR structure */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+ undi_isr->BufferLength = len;
+ undi_isr->FrameLength = len;
+ undi_isr->FrameHeaderLength = ll_hlen;
+ undi_isr->Frame.segment = rm_ds;
+ undi_isr->Frame.offset = __from_data16 ( basemem_packet );
+ undi_isr->ProtType = prottype;
+ undi_isr->PktType = XMT_DESTADDR;
+ DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
+ undi_isr->Frame.segment, undi_isr->Frame.offset,
+ undi_isr->BufferLength, undi_isr->FrameLength,
+ ( net_protocol ? net_protocol->name : "RAW" ),
+ undi_isr->FrameHeaderLength );
+
+ /* Free packet */
+ free_iob ( iobuf );
+ break;
+ default :
+ DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
+ undi_isr->FuncFlag );
+
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC2 ( &pxenv_undi_isr, "\n" );
+ undi_isr->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}