pxe_tftp.c

Go to the documentation of this file.
00001 /** @file
00002  *
00003  * PXE TFTP API
00004  *
00005  */
00006 
00007 /*
00008  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
00009  *
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023  */
00024 
00025 FILE_LICENCE ( GPL2_OR_LATER );
00026 
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <errno.h>
00030 #include <byteswap.h>
00031 #include <gpxe/uaccess.h>
00032 #include <gpxe/in.h>
00033 #include <gpxe/tftp.h>
00034 #include <gpxe/xfer.h>
00035 #include <gpxe/open.h>
00036 #include <gpxe/process.h>
00037 #include <pxe.h>
00038 
00039 /** A PXE TFTP connection */
00040 struct pxe_tftp_connection {
00041         /** Data transfer interface */
00042         struct xfer_interface xfer;
00043         /** Data buffer */
00044         userptr_t buffer;
00045         /** Size of data buffer */
00046         size_t size;
00047         /** Starting offset of data buffer */
00048         size_t start;
00049         /** File position */
00050         size_t offset;
00051         /** Maximum file position */
00052         size_t max_offset;
00053         /** Block size */
00054         size_t blksize;
00055         /** Block index */
00056         unsigned int blkidx;
00057         /** Overall return status code */
00058         int rc;
00059 };
00060 
00061 /** The PXE TFTP connection */
00062 static struct pxe_tftp_connection pxe_tftp = {
00063         .xfer = XFER_INIT ( &null_xfer_ops ),
00064 };
00065 
00066 /**
00067  * Close PXE TFTP connection
00068  *
00069  * @v rc                Final status code
00070  */
00071 static void pxe_tftp_close ( int rc ) {
00072         xfer_nullify ( &pxe_tftp.xfer );
00073         xfer_close ( &pxe_tftp.xfer, rc );
00074         pxe_tftp.rc = rc;
00075 }
00076 
00077 /**
00078  * Receive new data
00079  *
00080  * @v xfer              Data transfer interface
00081  * @v iobuf             I/O buffer
00082  * @v meta              Transfer metadata
00083  * @ret rc              Return status code
00084  */
00085 static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused,
00086                                        struct io_buffer *iobuf,
00087                                        struct xfer_metadata *meta ) {
00088         size_t len = iob_len ( iobuf );
00089         int rc = 0;
00090 
00091         /* Calculate new buffer position */
00092         if ( meta->whence != SEEK_CUR )
00093                 pxe_tftp.offset = 0;
00094         pxe_tftp.offset += meta->offset;
00095 
00096         /* Copy data block to buffer */
00097         if ( len == 0 ) {
00098                 /* No data (pure seek); treat as success */
00099         } else if ( pxe_tftp.offset < pxe_tftp.start ) {
00100                 DBG ( " buffer underrun at %zx (min %zx)",
00101                       pxe_tftp.offset, pxe_tftp.start );
00102                 rc = -ENOBUFS;
00103         } else if ( ( pxe_tftp.offset + len ) >
00104                     ( pxe_tftp.start + pxe_tftp.size ) ) {
00105                 DBG ( " buffer overrun at %zx (max %zx)",
00106                       ( pxe_tftp.offset + len ),
00107                       ( pxe_tftp.start + pxe_tftp.size ) );
00108                 rc = -ENOBUFS;
00109         } else {
00110                 copy_to_user ( pxe_tftp.buffer,
00111                                ( pxe_tftp.offset - pxe_tftp.start ),
00112                                iobuf->data, len );
00113         }
00114 
00115         /* Calculate new buffer position */
00116         pxe_tftp.offset += len;
00117 
00118         /* Record maximum offset as the file size */
00119         if ( pxe_tftp.max_offset < pxe_tftp.offset )
00120                 pxe_tftp.max_offset = pxe_tftp.offset;
00121 
00122         /* Terminate transfer on error */
00123         if ( rc != 0 )
00124                 pxe_tftp_close ( rc );
00125 
00126         free_iob ( iobuf );
00127         return rc;
00128 }
00129 
00130 /**
00131  * Handle close() event
00132  *
00133  * @v xfer              Data transfer interface
00134  * @v rc                Reason for close
00135  */
00136 static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
00137                                   int rc ) {
00138         pxe_tftp_close ( rc );
00139 }
00140 
00141 static struct xfer_interface_operations pxe_tftp_xfer_ops = {
00142         .close          = pxe_tftp_xfer_close,
00143         .vredirect      = xfer_vreopen,
00144         .window         = unlimited_xfer_window,
00145         .alloc_iob      = default_xfer_alloc_iob,
00146         .deliver_iob    = pxe_tftp_xfer_deliver_iob,
00147         .deliver_raw    = xfer_deliver_as_iob,
00148 };
00149 
00150 /**
00151  * Maximum length of a PXE TFTP URI
00152  *
00153  * The PXE TFTP API provides 128 characters for the filename; the
00154  * extra 128 bytes allow for the remainder of the URI.
00155  */
00156 #define PXE_TFTP_URI_LEN 256
00157 
00158 /**
00159  * Open PXE TFTP connection
00160  *
00161  * @v ipaddress         IP address
00162  * @v port              TFTP server port
00163  * @v filename          File name
00164  * @v blksize           Requested block size
00165  * @ret rc              Return status code
00166  */
00167 static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
00168                            const unsigned char *filename, size_t blksize,
00169                            int sizeonly ) {
00170         char uri_string[PXE_TFTP_URI_LEN];
00171         struct in_addr address;
00172         int rc;
00173 
00174         /* Reset PXE TFTP connection structure */
00175         memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
00176         xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL );
00177         pxe_tftp.rc = -EINPROGRESS;
00178 
00179         /* Construct URI string */
00180         address.s_addr = ipaddress;
00181         if ( ! port )
00182                 port = htons ( TFTP_PORT );
00183         if ( blksize < TFTP_DEFAULT_BLKSIZE )
00184                 blksize = TFTP_DEFAULT_BLKSIZE;
00185         snprintf ( uri_string, sizeof ( uri_string ),
00186                    "tftp%s://%s:%d%s%s?blksize=%zd",
00187                    sizeonly ? "size" : "",
00188                    inet_ntoa ( address ), ntohs ( port ),
00189                    ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
00190         DBG ( " %s", uri_string );
00191 
00192         /* Open PXE TFTP connection */
00193         if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer,
00194                                            uri_string ) ) != 0 ) {
00195                 DBG ( " could not open (%s)\n", strerror ( rc ) );
00196                 return rc;
00197         }
00198 
00199         return 0;
00200 }
00201 
00202 /**
00203  * TFTP OPEN
00204  *
00205  * @v tftp_open                         Pointer to a struct s_PXENV_TFTP_OPEN
00206  * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
00207  * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
00208  * @v s_PXENV_TFTP_OPEN::FileName       Name of file to open
00209  * @v s_PXENV_TFTP_OPEN::TFTPPort       TFTP server UDP port
00210  * @v s_PXENV_TFTP_OPEN::PacketSize     TFTP blksize option to request
00211  * @ret #PXENV_EXIT_SUCCESS             File was opened
00212  * @ret #PXENV_EXIT_FAILURE             File was not opened
00213  * @ret s_PXENV_TFTP_OPEN::Status       PXE status code
00214  * @ret s_PXENV_TFTP_OPEN::PacketSize   Negotiated blksize
00215  * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small
00216  *
00217  * Opens a TFTP connection for downloading a file a block at a time
00218  * using pxenv_tftp_read().
00219  *
00220  * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
00221  * routing will take place.  See the relevant
00222  * @ref pxe_routing "implementation note" for more details.
00223  *
00224  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
00225  * value before calling this function in protected mode.  You cannot
00226  * call this function with a 32-bit stack segment.  (See the relevant
00227  * @ref pxe_x86_pmode16 "implementation note" for more details.)
00228  * 
00229  * @note According to the PXE specification version 2.1, this call
00230  * "opens a file for reading/writing", though how writing is to be
00231  * achieved without the existence of an API call %pxenv_tftp_write()
00232  * is not made clear.
00233  *
00234  * @note Despite the existence of the numerous statements within the
00235  * PXE specification of the form "...if a TFTP/MTFTP or UDP connection
00236  * is active...", you cannot use pxenv_tftp_open() and
00237  * pxenv_tftp_read() to read a file via MTFTP; only via plain old
00238  * TFTP.  If you want to use MTFTP, use pxenv_tftp_read_file()
00239  * instead.  Astute readers will note that, since
00240  * pxenv_tftp_read_file() is an atomic operation from the point of
00241  * view of the PXE API, it is conceptually impossible to issue any
00242  * other PXE API call "if an MTFTP connection is active".
00243  */
00244 PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
00245         int rc;
00246 
00247         DBG ( "PXENV_TFTP_OPEN" );
00248 
00249         /* Guard against callers that fail to close before re-opening */
00250         pxe_tftp_close ( 0 );
00251 
00252         /* Open connection */
00253         if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
00254                                     tftp_open->TFTPPort,
00255                                     tftp_open->FileName,
00256                                     tftp_open->PacketSize,
00257                                     0) ) != 0 ) {
00258                 tftp_open->Status = PXENV_STATUS ( rc );
00259                 return PXENV_EXIT_FAILURE;
00260         }
00261 
00262         /* Wait for OACK to arrive so that we have the block size */
00263         while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
00264                 ( pxe_tftp.max_offset == 0 ) ) {
00265                 step();
00266         }
00267         pxe_tftp.blksize = xfer_window ( &pxe_tftp.xfer );
00268         tftp_open->PacketSize = pxe_tftp.blksize;
00269         DBG ( " blksize=%d", tftp_open->PacketSize );
00270 
00271         /* EINPROGRESS is normal; we don't wait for the whole transfer */
00272         if ( rc == -EINPROGRESS )
00273                 rc = 0;
00274 
00275         tftp_open->Status = PXENV_STATUS ( rc );
00276         return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
00277 }
00278 
00279 /**
00280  * TFTP CLOSE
00281  *
00282  * @v tftp_close                        Pointer to a struct s_PXENV_TFTP_CLOSE
00283  * @ret #PXENV_EXIT_SUCCESS             File was closed successfully
00284  * @ret #PXENV_EXIT_FAILURE             File was not closed
00285  * @ret s_PXENV_TFTP_CLOSE::Status      PXE status code
00286  * @err None                            -
00287  *
00288  * Close a connection previously opened with pxenv_tftp_open().  You
00289  * must have previously opened a connection with pxenv_tftp_open().
00290  *
00291  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
00292  * value before calling this function in protected mode.  You cannot
00293  * call this function with a 32-bit stack segment.  (See the relevant
00294  * @ref pxe_x86_pmode16 "implementation note" for more details.)
00295  */
00296 PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
00297         DBG ( "PXENV_TFTP_CLOSE" );
00298 
00299         pxe_tftp_close ( 0 );
00300         tftp_close->Status = PXENV_STATUS_SUCCESS;
00301         return PXENV_EXIT_SUCCESS;
00302 }
00303 
00304 /**
00305  * TFTP READ
00306  *
00307  * @v tftp_read                         Pointer to a struct s_PXENV_TFTP_READ
00308  * @v s_PXENV_TFTP_READ::Buffer         Address of data buffer
00309  * @ret #PXENV_EXIT_SUCCESS             Data was read successfully
00310  * @ret #PXENV_EXIT_FAILURE             Data was not read
00311  * @ret s_PXENV_TFTP_READ::Status       PXE status code
00312  * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number
00313  * @ret s_PXENV_TFTP_READ::BufferSize   Length of data written into buffer
00314  *
00315  * Reads a single packet from a connection previously opened with
00316  * pxenv_tftp_open() into the data buffer pointed to by
00317  * s_PXENV_TFTP_READ::Buffer.  You must have previously opened a
00318  * connection with pxenv_tftp_open().  The data written into
00319  * s_PXENV_TFTP_READ::Buffer is just the file data; the various
00320  * network headers have already been removed.
00321  *
00322  * The buffer must be large enough to contain a packet of the size
00323  * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the
00324  * pxenv_tftp_open() call.  It is worth noting that the PXE
00325  * specification does @b not require the caller to fill in
00326  * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so
00327  * the PXE stack is free to ignore whatever value the caller might
00328  * place there and just assume that the buffer is large enough.  That
00329  * said, it may be worth the caller always filling in
00330  * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that
00331  * mistake it for an input parameter.
00332  *
00333  * The length of the TFTP data packet will be returned via
00334  * s_PXENV_TFTP_READ::BufferSize.  If this length is less than the
00335  * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to
00336  * pxenv_tftp_open(), this indicates that the block is the last block
00337  * in the file.  Note that zero is a valid length for
00338  * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of
00339  * the file is a multiple of the blksize.
00340  *
00341  * The PXE specification doesn't actually state that calls to
00342  * pxenv_tftp_read() will return the data packets in strict sequential
00343  * order, though most PXE stacks will probably do so.  The sequence
00344  * number of the packet will be returned in
00345  * s_PXENV_TFTP_READ::PacketNumber.  The first packet in the file has
00346  * a sequence number of one, not zero.
00347  *
00348  * To guard against flawed PXE stacks, the caller should probably set
00349  * s_PXENV_TFTP_READ::PacketNumber to one less than the expected
00350  * returned value (i.e. set it to zero for the first call to
00351  * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ
00352  * parameter block for subsequent calls without modifying
00353  * s_PXENV_TFTP_READ::PacketNumber between calls).  The caller should
00354  * also guard against potential problems caused by flawed
00355  * implementations returning the occasional duplicate packet, by
00356  * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber
00357  * is as expected (i.e. one greater than that returned from the
00358  * previous call to pxenv_tftp_read()).
00359  *
00360  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
00361  * value before calling this function in protected mode.  You cannot
00362  * call this function with a 32-bit stack segment.  (See the relevant
00363  * @ref pxe_x86_pmode16 "implementation note" for more details.)
00364  */
00365 PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
00366         int rc;
00367 
00368         DBG ( "PXENV_TFTP_READ to %04x:%04x",
00369               tftp_read->Buffer.segment, tftp_read->Buffer.offset );
00370 
00371         /* Read single block into buffer */
00372         pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
00373                                          tftp_read->Buffer.offset );
00374         pxe_tftp.size = pxe_tftp.blksize;
00375         pxe_tftp.start = pxe_tftp.offset;
00376         while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
00377                 ( pxe_tftp.offset == pxe_tftp.start ) )
00378                 step();
00379         pxe_tftp.buffer = UNULL;
00380         tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
00381         tftp_read->PacketNumber = ++pxe_tftp.blkidx;
00382 
00383         /* EINPROGRESS is normal if we haven't reached EOF yet */
00384         if ( rc == -EINPROGRESS )
00385                 rc = 0;
00386 
00387         tftp_read->Status = PXENV_STATUS ( rc );
00388         return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
00389 }
00390 
00391 /**
00392  * TFTP/MTFTP read file
00393  *
00394  * @v tftp_read_file                 Pointer to a struct s_PXENV_TFTP_READ_FILE
00395  * @v s_PXENV_TFTP_READ_FILE::FileName          File name
00396  * @v s_PXENV_TFTP_READ_FILE::BufferSize        Size of the receive buffer
00397  * @v s_PXENV_TFTP_READ_FILE::Buffer            Address of the receive buffer
00398  * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress   TFTP server IP address
00399  * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress  Relay agent IP address
00400  * @v s_PXENV_TFTP_READ_FILE::McastIPAddress    File's multicast IP address
00401  * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort      Client multicast UDP port
00402  * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort       Server multicast UDP port
00403  * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut   Time to wait for first packet
00404  * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay   MTFTP inactivity timeout
00405  * @ret #PXENV_EXIT_SUCCESS                     File downloaded successfully
00406  * @ret #PXENV_EXIT_FAILURE                     File not downloaded
00407  * @ret s_PXENV_TFTP_READ_FILE::Status          PXE status code
00408  * @ret s_PXENV_TFTP_READ_FILE::BufferSize      Length of downloaded file
00409  *
00410  * Downloads an entire file via either TFTP or MTFTP into the buffer
00411  * pointed to by s_PXENV_TFTP_READ_FILE::Buffer.
00412  *
00413  * The PXE specification does not make it clear how the caller
00414  * requests that MTFTP be used rather than TFTP (or vice versa).  One
00415  * reasonable guess is that setting
00416  * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP
00417  * to be used instead of MTFTP, though it is conceivable that some PXE
00418  * stacks would interpret that as "use the DHCP-provided multicast IP
00419  * address" instead.  Some PXE stacks will not implement MTFTP at all,
00420  * and will always use TFTP.
00421  *
00422  * It is not specified whether or not
00423  * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server
00424  * port for TFTP (rather than MTFTP) downloads.  Callers should assume
00425  * that the only way to access a TFTP server on a non-standard port is
00426  * to use pxenv_tftp_open() and pxenv_tftp_read().
00427  *
00428  * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP
00429  * routing will take place.  See the relevant
00430  * @ref pxe_routing "implementation note" for more details.
00431  *
00432  * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an
00433  * #ADDR32_t type, i.e. nominally a flat physical address.  Some PXE
00434  * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real
00435  * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above
00436  * 1MB.  This means that PXE stacks must be prepared to write to areas
00437  * outside base memory.  Exactly how this is to be achieved is not
00438  * specified, though using INT 15,87 is as close to a standard method
00439  * as any, and should probably be used.  Switching to protected-mode
00440  * in order to access high memory will fail if pxenv_tftp_read_file()
00441  * is called in V86 mode; it is reasonably to expect that a V86
00442  * monitor would intercept the relatively well-defined INT 15,87 if it
00443  * wants the PXE stack to be able to write to high memory.
00444  *
00445  * Things get even more interesting if pxenv_tftp_read_file() is
00446  * called in protected mode, because there is then absolutely no way
00447  * for the PXE stack to write to an absolute physical address.  You
00448  * can't even get around the problem by creating a special "access
00449  * everything" segment in the s_PXE data structure, because the
00450  * #SEGDESC_t descriptors are limited to 64kB in size.
00451  *
00452  * Previous versions of the PXE specification (e.g. WfM 1.1a) provide
00453  * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to
00454  * work around this problem.  The s_PXENV_TFTP_READ_FILE_PMODE
00455  * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into
00456  * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and
00457  * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a
00458  * protected-mode segment:offset address for the data buffer.  This
00459  * API call is no longer present in version 2.1 of the PXE
00460  * specification.
00461  *
00462  * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer
00463  * is an offset relative to the caller's data segment, when
00464  * pxenv_tftp_read_file() is called in protected mode.
00465  *
00466  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
00467  * value before calling this function in protected mode.  You cannot
00468  * call this function with a 32-bit stack segment.  (See the relevant
00469  * @ref pxe_x86_pmode16 "implementation note" for more details.)
00470  */
00471 PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
00472                                     *tftp_read_file ) {
00473         int rc;
00474 
00475         DBG ( "PXENV_TFTP_READ_FILE to %08x+%x", tftp_read_file->Buffer,
00476               tftp_read_file->BufferSize );
00477 
00478         /* Open TFTP file */
00479         if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
00480                                     tftp_read_file->FileName, 0, 0 ) ) != 0 ) {
00481                 tftp_read_file->Status = PXENV_STATUS ( rc );
00482                 return PXENV_EXIT_FAILURE;
00483         }
00484 
00485         /* Read entire file */
00486         pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
00487         pxe_tftp.size = tftp_read_file->BufferSize;
00488         while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
00489                 step();
00490         pxe_tftp.buffer = UNULL;
00491         tftp_read_file->BufferSize = pxe_tftp.max_offset;
00492 
00493         /* Close TFTP file */
00494         pxe_tftp_close ( rc );
00495 
00496         tftp_read_file->Status = PXENV_STATUS ( rc );
00497         return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
00498 }
00499 
00500 /**
00501  * TFTP GET FILE SIZE
00502  *
00503  * @v tftp_get_fsize                 Pointer to a struct s_PXENV_TFTP_GET_FSIZE
00504  * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress   TFTP server IP address
00505  * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress  Relay agent IP address
00506  * @v s_PXENV_TFTP_GET_FSIZE::FileName  File name
00507  * @ret #PXENV_EXIT_SUCCESS             File size was determined successfully
00508  * @ret #PXENV_EXIT_FAILURE             File size was not determined
00509  * @ret s_PXENV_TFTP_GET_FSIZE::Status  PXE status code
00510  * @ret s_PXENV_TFTP_GET_FSIZE::FileSize        File size
00511  *
00512  * Determine the size of a file on a TFTP server.  This uses the
00513  * "tsize" TFTP option, and so will not work with a TFTP server that
00514  * does not support TFTP options, or that does not support the "tsize"
00515  * option.
00516  *
00517  * The PXE specification states that this API call will @b not open a
00518  * TFTP connection for subsequent use with pxenv_tftp_read().  (This
00519  * is somewhat daft, since the only way to obtain the file size via
00520  * the "tsize" option involves issuing a TFTP open request, but that's
00521  * life.)
00522  *
00523  * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP
00524  * connection is open.
00525  *
00526  * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP
00527  * routing will take place.  See the relevant
00528  * @ref pxe_routing "implementation note" for more details.
00529  *
00530  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
00531  * value before calling this function in protected mode.  You cannot
00532  * call this function with a 32-bit stack segment.  (See the relevant
00533  * @ref pxe_x86_pmode16 "implementation note" for more details.)
00534  * 
00535  * @note There is no way to specify the TFTP server port with this API
00536  * call.  Though you can open a file using a non-standard TFTP server
00537  * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially,
00538  * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of
00539  * a file from a TFTP server listening on the standard TFTP port.
00540  * "Consistency" is not a word in Intel's vocabulary.
00541  */
00542 PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
00543                                     *tftp_get_fsize ) {
00544         int rc;
00545 
00546         DBG ( "PXENV_TFTP_GET_FSIZE" );
00547 
00548         /* Open TFTP file */
00549         if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
00550                                     tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) {
00551                 tftp_get_fsize->Status = PXENV_STATUS ( rc );
00552                 return PXENV_EXIT_FAILURE;
00553         }
00554 
00555         /* Wait for initial seek to arrive, and record size */
00556         while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
00557                 ( pxe_tftp.max_offset == 0 ) ) {
00558                 step();
00559         }
00560         tftp_get_fsize->FileSize = pxe_tftp.max_offset;
00561         DBG ( " fsize=%d", tftp_get_fsize->FileSize );
00562 
00563         /* EINPROGRESS is normal; we don't wait for the whole transfer */
00564         if ( rc == -EINPROGRESS )
00565                 rc = 0;
00566 
00567         /* Close TFTP file */
00568         pxe_tftp_close ( rc );
00569 
00570         tftp_get_fsize->Status = PXENV_STATUS ( rc );
00571         return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
00572 }

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1