mirror of
https://github.com/ipxe/ipxe
synced 2025-12-17 10:01:03 +03:00
[pxe] Move all PXE files to arch/i386
The initial PXE implementation in Etherboot had the goal of being architecture-agnostic, but this goal has not been realised.
This commit is contained in:
@@ -1,103 +0,0 @@
|
||||
#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" },
|
||||
};
|
||||
@@ -1,264 +0,0 @@
|
||||
/** @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>
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
file_api_check->Flags = 0; /* None defined */
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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 );
|
||||
|
||||
/* Set up PXE data structures */
|
||||
pxe_init_structures();
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE Preboot API
|
||||
*
|
||||
*/
|
||||
|
||||
/* PXE API interface for Etherboot.
|
||||
*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <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 "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_fakeproxydhcpack },
|
||||
};
|
||||
|
||||
/* 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 );
|
||||
|
||||
/* Save as PXE net device */
|
||||
pxe_set_netdev ( netdev );
|
||||
|
||||
/* Hook INT 1A */
|
||||
pxe_hook_int1a();
|
||||
|
||||
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" );
|
||||
|
||||
/* Unhook INT 1A */
|
||||
pxe_unhook_int1a();
|
||||
|
||||
/* Clear PXE net device */
|
||||
pxe_set_netdev ( NULL );
|
||||
|
||||
/* Prepare for unload */
|
||||
shutdown ( SHUTDOWN_BOOT );
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,584 +0,0 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE TFTP API
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <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;
|
||||
|
||||
/* Mildly ugly hack; assume that the first non-zero seek
|
||||
* indicates the block size.
|
||||
*/
|
||||
if ( pxe_tftp.blksize == 0 )
|
||||
pxe_tftp.blksize = pxe_tftp.offset;
|
||||
|
||||
/* 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_vopen,
|
||||
.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 ) {
|
||||
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:%d%s%s?blksize=%zd",
|
||||
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 ) {
|
||||
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.blksize == 0 ) ) {
|
||||
step();
|
||||
}
|
||||
tftp_open->PacketSize = pxe_tftp.blksize;
|
||||
|
||||
/* 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 %08lx+%lx", 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 ) {
|
||||
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 ) ) != 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;
|
||||
|
||||
/* 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 );
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
/** @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.
|
||||
*/
|
||||
|
||||
/** 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;
|
||||
}
|
||||
@@ -1,684 +0,0 @@
|
||||
/** @file
|
||||
*
|
||||
* PXE UNDI API
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <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;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_STARTUP
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
|
||||
DBG ( "PXENV_UNDI_STARTUP" );
|
||||
|
||||
undi_startup->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_CLEANUP
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
|
||||
DBG ( "PXENV_UNDI_CLEANUP" );
|
||||
|
||||
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" );
|
||||
|
||||
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_netdev_close();
|
||||
if ( ( rc = pxe_netdev_open() ) != 0 ) {
|
||||
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" );
|
||||
|
||||
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" );
|
||||
|
||||
if ( ( rc = pxe_netdev_open() ) != 0 ) {
|
||||
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" );
|
||||
|
||||
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;
|
||||
|
||||
DBG ( "PXENV_UNDI_TRANSMIT" );
|
||||
|
||||
/* 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:
|
||||
undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
DBG ( " %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;
|
||||
DBG ( " %d", tbd.ImmedLength );
|
||||
for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
|
||||
datablk = &tbd.DataBlock[i];
|
||||
len += datablk->TDDataLen;
|
||||
DBG ( "+%d", datablk->TDDataLen );
|
||||
}
|
||||
|
||||
/* Allocate and fill I/O buffer */
|
||||
iobuf = alloc_iob ( ll_hlen + len );
|
||||
if ( ! iobuf ) {
|
||||
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;
|
||||
} else {
|
||||
DBG ( " BCAST" );
|
||||
ll_dest = ll_protocol->ll_broadcast;
|
||||
}
|
||||
|
||||
/* Add link-layer header */
|
||||
if ( ( rc = ll_protocol->push ( iobuf, ll_dest,
|
||||
pxe_netdev->ll_addr,
|
||||
net_protocol->net_proto ))!=0){
|
||||
free_iob ( iobuf );
|
||||
undi_transmit->Status = PXENV_STATUS ( rc );
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transmit packet */
|
||||
if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
|
||||
undi_transmit->Status = PXENV_STATUS ( rc );
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Flag transmission as in-progress */
|
||||
undi_tx_count++;
|
||||
|
||||
undi_transmit->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_SET_MCAST_ADDRESS
|
||||
*
|
||||
* Status: stub (no PXE multicast support)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
|
||||
*undi_set_mcast_address ) {
|
||||
DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
|
||||
|
||||
undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* 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 ) {
|
||||
|
||||
DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
|
||||
|
||||
/* If adapter is open, the change will have no effect; return
|
||||
* an error
|
||||
*/
|
||||
if ( pxe_netdev->state & NETDEV_OPEN ) {
|
||||
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,
|
||||
pxe_netdev->ll_protocol->ll_addr_len );
|
||||
|
||||
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" );
|
||||
|
||||
undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_INFORMATION
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
|
||||
*undi_get_information ) {
|
||||
struct device *dev = pxe_netdev->dev;
|
||||
struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
|
||||
|
||||
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_protocol->ll_addr_len;
|
||||
/* Cheat: assume card is always configured with its permanent
|
||||
* node address. This is a valid assumption within Etherboot
|
||||
* at the time of writing.
|
||||
*/
|
||||
memcpy ( &undi_get_information->CurrentNodeAddress,
|
||||
pxe_netdev->ll_addr,
|
||||
sizeof ( undi_get_information->CurrentNodeAddress ) );
|
||||
memcpy ( &undi_get_information->PermNodeAddress,
|
||||
pxe_netdev->ll_addr,
|
||||
sizeof ( 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;
|
||||
|
||||
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;
|
||||
|
||||
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" );
|
||||
|
||||
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" );
|
||||
|
||||
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" );
|
||||
|
||||
undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_MCAST_ADDRESS
|
||||
*
|
||||
* Status: stub (no PXE multicast support)
|
||||
*/
|
||||
PXENV_EXIT_t
|
||||
pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
|
||||
*undi_get_mcast_address ) {
|
||||
DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
|
||||
|
||||
undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_NIC_TYPE
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
|
||||
*undi_get_nic_type ) {
|
||||
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;
|
||||
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.
|
||||
*/
|
||||
break; }
|
||||
default:
|
||||
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 ), "gPXE" );
|
||||
undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
|
||||
undi_get_iface_info->ServiceFlags = 0;
|
||||
memset ( undi_get_iface_info->Reserved, 0,
|
||||
sizeof(undi_get_iface_info->Reserved) );
|
||||
|
||||
undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* PXENV_UNDI_GET_STATE
|
||||
*
|
||||
* Status: impossible
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
|
||||
*undi_get_state ) {
|
||||
DBG ( "PXENV_UNDI_GET_STATE" );
|
||||
|
||||
undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
};
|
||||
|
||||
/* PXENV_UNDI_ISR
|
||||
*
|
||||
* Status: working
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
||||
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;
|
||||
|
||||
DBG ( "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 :
|
||||
DBG ( " 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 */
|
||||
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
|
||||
break;
|
||||
case PXENV_UNDI_ISR_IN_PROCESS :
|
||||
DBG ( " PROCESS" );
|
||||
/* Fall through */
|
||||
case PXENV_UNDI_ISR_IN_GET_NEXT :
|
||||
DBG ( " 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 ) ) {
|
||||
DBG ( " 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 ) {
|
||||
DBG ( " 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 );
|
||||
DBG ( " RX %zd", len );
|
||||
if ( len > sizeof ( basemem_packet ) ) {
|
||||
/* Should never happen */
|
||||
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 ( 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;
|
||||
}
|
||||
DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Free packet */
|
||||
free_iob ( iobuf );
|
||||
break;
|
||||
default :
|
||||
DBG ( " INVALID(%04x)", 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;
|
||||
}
|
||||
|
||||
undi_isr->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user