iPXE
pxe_preboot.c
Go to the documentation of this file.
00001 /** @file
00002  *
00003  * PXE Preboot API
00004  *
00005  */
00006 
00007 /* PXE API interface for Etherboot.
00008  *
00009  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License as
00013  * published by the Free Software Foundation; either version 2 of the
00014  * License, or any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00024  * 02110-1301, USA.
00025  *
00026  * You can also choose to distribute this program under the terms of
00027  * the Unmodified Binary Distribution Licence (as given in the file
00028  * COPYING.UBDL), provided that you have satisfied its requirements.
00029  */
00030 
00031 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00032 
00033 #include <stdint.h>
00034 #include <string.h>
00035 #include <stdlib.h>
00036 #include <ipxe/uaccess.h>
00037 #include <ipxe/dhcp.h>
00038 #include <ipxe/fakedhcp.h>
00039 #include <ipxe/device.h>
00040 #include <ipxe/netdevice.h>
00041 #include <ipxe/isapnp.h>
00042 #include <ipxe/init.h>
00043 #include <ipxe/if_ether.h>
00044 #include <basemem_packet.h>
00045 #include <biosint.h>
00046 #include <rmsetjmp.h>
00047 #include "pxe.h"
00048 #include "pxe_call.h"
00049 
00050 /* Avoid dragging in isapnp.o unnecessarily */
00051 uint16_t isapnp_read_port;
00052 
00053 /** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
00054 enum pxe_cached_info_indices {
00055         CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ),
00056         CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ),
00057         CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ),
00058         NUM_CACHED_INFOS
00059 };
00060 
00061 /** A cached DHCP packet */
00062 union pxe_cached_info {
00063         struct dhcphdr dhcphdr;
00064         /* This buffer must be *exactly* the size of a BOOTPLAYER_t
00065          * structure, otherwise WinPE will die horribly.  It takes the
00066          * size of *our* buffer and feeds it in to us as the size of
00067          * one of *its* buffers.  If our buffer is larger than it
00068          * expects, we therefore end up overwriting part of its data
00069          * segment, since it tells us to do so.  (D'oh!)
00070          *
00071          * Note that a BOOTPLAYER_t is not necessarily large enough to
00072          * hold a DHCP packet; this is a flaw in the PXE spec.
00073          */
00074         BOOTPLAYER_t packet;
00075 } __attribute__ (( packed ));
00076 
00077 /** A PXE DHCP packet creator */
00078 struct pxe_dhcp_packet_creator {
00079         /** Create DHCP packet
00080          *
00081          * @v netdev            Network device
00082          * @v data              Buffer for DHCP packet
00083          * @v max_len           Size of DHCP packet buffer
00084          * @ret rc              Return status code
00085          */
00086         int ( * create ) ( struct net_device *netdev, void *data,
00087                            size_t max_len );
00088 };
00089 
00090 /** PXE DHCP packet creators */
00091 static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = {
00092         [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover },
00093         [CACHED_INFO_DHCPACK] = { create_fakedhcpack },
00094         [CACHED_INFO_BINL] = { create_fakepxebsack },
00095 };
00096 
00097 /**
00098  * Name PXENV_GET_CACHED_INFO packet type
00099  *
00100  * @v packet_type       Packet type
00101  * @ret name            Name of packet type
00102  */
00103 static inline __attribute__ (( always_inline )) const char *
00104 pxenv_get_cached_info_name ( int packet_type ) {
00105         switch ( packet_type ) {
00106         case PXENV_PACKET_TYPE_DHCP_DISCOVER:
00107                 return "DHCPDISCOVER";
00108         case PXENV_PACKET_TYPE_DHCP_ACK:
00109                 return "DHCPACK";
00110         case PXENV_PACKET_TYPE_CACHED_REPLY:
00111                 return "BINL";
00112         default:
00113                 return "<INVALID>";
00114         }
00115 }
00116 
00117 /* The case in which the caller doesn't supply a buffer is really
00118  * awkward to support given that we have multiple sources of options,
00119  * and that we don't actually store the DHCP packets.  (We may not
00120  * even have performed DHCP; we may have obtained all configuration
00121  * from non-volatile stored options or from the command line.)
00122  *
00123  * Some NBPs rely on the buffers we provide being persistent, so we
00124  * can't just use the temporary packet buffer.  4.5kB of base memory
00125  * always wasted just because some clients are too lazy to provide
00126  * their own buffers...
00127  */
00128 static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] );
00129 #define cached_info __use_data16 ( cached_info )
00130 
00131 /**
00132  * Construct cached DHCP packets
00133  *
00134  */
00135 void pxe_fake_cached_info ( void ) {
00136         struct pxe_dhcp_packet_creator *creator;
00137         union pxe_cached_info *info;
00138         unsigned int i;
00139         int rc;
00140 
00141         /* Sanity check */
00142         assert ( pxe_netdev != NULL );
00143 
00144         /* Erase any stale packets */
00145         memset ( cached_info, 0, sizeof ( cached_info ) );
00146 
00147         /* Construct all DHCP packets */
00148         for ( i = 0 ; i < ( sizeof ( pxe_dhcp_packet_creators ) /
00149                             sizeof ( pxe_dhcp_packet_creators[0] ) ) ; i++ ) {
00150 
00151                 /* Construct DHCP packet */
00152                 creator = &pxe_dhcp_packet_creators[i];
00153                 info = &cached_info[i];
00154                 if ( ( rc = creator->create ( pxe_netdev, info,
00155                                               sizeof ( *info ) ) ) != 0 ) {
00156                         DBGC ( &pxe_netdev, " failed to build packet: %s\n",
00157                                strerror ( rc ) );
00158                         /* Continue constructing remaining packets */
00159                 }
00160         }
00161 }
00162 
00163 /**
00164  * UNLOAD BASE CODE STACK
00165  *
00166  * @v None                              -
00167  * @ret ...
00168  *
00169  */
00170 static PXENV_EXIT_t
00171 pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
00172         DBGC ( &pxe_netdev, "PXENV_UNLOAD_STACK\n" );
00173 
00174         unload_stack->Status = PXENV_STATUS_SUCCESS;
00175         return PXENV_EXIT_SUCCESS;
00176 }
00177 
00178 /* PXENV_GET_CACHED_INFO
00179  *
00180  * Status: working
00181  */
00182 static PXENV_EXIT_t
00183 pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) {
00184         union pxe_cached_info *info;
00185         unsigned int idx;
00186         size_t len;
00187         userptr_t buffer;
00188 
00189         DBGC ( &pxe_netdev, "PXENV_GET_CACHED_INFO %s to %04x:%04x+%x",
00190                pxenv_get_cached_info_name ( get_cached_info->PacketType ),
00191                get_cached_info->Buffer.segment,
00192                get_cached_info->Buffer.offset, get_cached_info->BufferSize );
00193 
00194         /* Sanity check */
00195         idx = ( get_cached_info->PacketType - 1 );
00196         if ( idx >= NUM_CACHED_INFOS ) {
00197                 DBGC ( &pxe_netdev, " bad PacketType %d\n",
00198                        get_cached_info->PacketType );
00199                 get_cached_info->Status = PXENV_STATUS_UNSUPPORTED;
00200                 return PXENV_EXIT_FAILURE;
00201         }
00202         info = &cached_info[idx];
00203 
00204         /* Copy packet (if applicable) */
00205         len = get_cached_info->BufferSize;
00206         if ( len == 0 ) {
00207                 /* Point client at our cached buffer.
00208                  *
00209                  * To add to the fun, Intel decided at some point in
00210                  * the evolution of the PXE specification to add the
00211                  * BufferLimit field, which we are meant to fill in
00212                  * with the length of our packet buffer, so that the
00213                  * caller can safely modify the boot server reply
00214                  * packet stored therein.  However, this field was not
00215                  * present in earlier versions of the PXE spec, and
00216                  * there is at least one PXE NBP (Altiris) which
00217                  * allocates only exactly enough space for this
00218                  * earlier, shorter version of the structure.  If we
00219                  * actually fill in the BufferLimit field, we
00220                  * therefore risk trashing random areas of the
00221                  * caller's memory.  If we *don't* fill it in, then
00222                  * the caller is at liberty to assume that whatever
00223                  * random value happened to be in that location
00224                  * represents the length of the buffer we've just
00225                  * passed back to it.
00226                  *
00227                  * Since older PXE stacks won't fill this field in
00228                  * anyway, it's probably safe to assume that no
00229                  * callers actually rely on it, so we choose to not
00230                  * fill it in.
00231                  */
00232                 get_cached_info->Buffer.segment = rm_ds;
00233                 get_cached_info->Buffer.offset = __from_data16 ( info );
00234                 get_cached_info->BufferSize = sizeof ( *info );
00235                 DBGC ( &pxe_netdev, " using %04x:%04x+%04x['%x']",
00236                        get_cached_info->Buffer.segment,
00237                        get_cached_info->Buffer.offset,
00238                        get_cached_info->BufferSize,
00239                        get_cached_info->BufferLimit );
00240         } else {
00241                 /* Copy packet to client buffer */
00242                 if ( len > sizeof ( *info ) )
00243                         len = sizeof ( *info );
00244                 if ( len < sizeof ( *info ) )
00245                         DBGC ( &pxe_netdev, " buffer may be too short" );
00246                 buffer = real_to_user ( get_cached_info->Buffer.segment,
00247                                         get_cached_info->Buffer.offset );
00248                 copy_to_user ( buffer, 0, info, len );
00249                 get_cached_info->BufferSize = len;
00250         }
00251 
00252         DBGC ( &pxe_netdev, "\n" );
00253         get_cached_info->Status = PXENV_STATUS_SUCCESS;
00254         return PXENV_EXIT_SUCCESS;
00255 }
00256 
00257 /* PXENV_RESTART_TFTP
00258  *
00259  * Status: working
00260  */
00261 static PXENV_EXIT_t
00262 pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE *restart_tftp ) {
00263         PXENV_EXIT_t tftp_exit;
00264 
00265         DBGC ( &pxe_netdev, "PXENV_RESTART_TFTP\n" );
00266 
00267         /* Words cannot describe the complete mismatch between the PXE
00268          * specification and any possible version of reality...
00269          */
00270         restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
00271         restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
00272         tftp_exit = pxenv_tftp_read_file ( restart_tftp );
00273         if ( tftp_exit != PXENV_EXIT_SUCCESS )
00274                 return tftp_exit;
00275 
00276         /* Restart NBP */
00277         rmlongjmp ( pxe_restart_nbp, PXENV_RESTART_TFTP );
00278 }
00279 
00280 /* PXENV_START_UNDI
00281  *
00282  * Status: working
00283  */
00284 static PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
00285         unsigned int bus_type;
00286         unsigned int location;
00287         struct net_device *netdev;
00288 
00289         DBGC ( &pxe_netdev, "PXENV_START_UNDI %04x:%04x:%04x\n",
00290                start_undi->AX, start_undi->BX, start_undi->DX );
00291 
00292         /* Determine bus type and location.  Use a heuristic to decide
00293          * whether we are PCI or ISAPnP
00294          */
00295         if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
00296              ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
00297              ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
00298              ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
00299                 bus_type = BUS_TYPE_ISAPNP;
00300                 location = start_undi->BX;
00301                 /* Record ISAPnP read port for use by isapnp.c */
00302                 isapnp_read_port = start_undi->DX;
00303         } else {
00304                 bus_type = BUS_TYPE_PCI;
00305                 location = start_undi->AX;
00306         }
00307 
00308         /* Probe for devices, etc. */
00309         startup();
00310 
00311         /* Look for a matching net device */
00312         netdev = find_netdev_by_location ( bus_type, location );
00313         if ( ! netdev ) {
00314                 DBGC ( &pxe_netdev, "PXENV_START_UNDI could not find matching "
00315                        "net device\n" );
00316                 start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
00317                 return PXENV_EXIT_FAILURE;
00318         }
00319         DBGC ( &pxe_netdev, "PXENV_START_UNDI found net device %s\n",
00320                netdev->name );
00321 
00322         /* Activate PXE */
00323         pxe_activate ( netdev );
00324 
00325         start_undi->Status = PXENV_STATUS_SUCCESS;
00326         return PXENV_EXIT_SUCCESS;
00327 }
00328 
00329 /* PXENV_STOP_UNDI
00330  *
00331  * Status: working
00332  */
00333 static PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
00334         DBGC ( &pxe_netdev, "PXENV_STOP_UNDI\n" );
00335 
00336         /* Deactivate PXE */
00337         pxe_deactivate();
00338 
00339         /* Prepare for unload */
00340         shutdown_boot();
00341 
00342         /* Check to see if we still have any hooked interrupts */
00343         if ( hooked_bios_interrupts != 0 ) {
00344                 DBGC ( &pxe_netdev, "PXENV_STOP_UNDI failed: %d interrupts "
00345                        "still hooked\n", hooked_bios_interrupts );
00346                 stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
00347                 return PXENV_EXIT_FAILURE;
00348         }
00349 
00350         stop_undi->Status = PXENV_STATUS_SUCCESS;
00351         return PXENV_EXIT_SUCCESS;
00352 }
00353 
00354 /* PXENV_START_BASE
00355  *
00356  * Status: won't implement (requires major structural changes)
00357  */
00358 static PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
00359         DBGC ( &pxe_netdev, "PXENV_START_BASE\n" );
00360 
00361         start_base->Status = PXENV_STATUS_UNSUPPORTED;
00362         return PXENV_EXIT_FAILURE;
00363 }
00364 
00365 /* PXENV_STOP_BASE
00366  *
00367  * Status: working
00368  */
00369 static PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
00370         DBGC ( &pxe_netdev, "PXENV_STOP_BASE\n" );
00371 
00372         /* The only time we will be called is when the NBP is trying
00373          * to shut down the PXE stack.  There's nothing we need to do
00374          * in this call.
00375          */
00376 
00377         stop_base->Status = PXENV_STATUS_SUCCESS;
00378         return PXENV_EXIT_SUCCESS;
00379 }
00380 
00381 /** PXE preboot API */
00382 struct pxe_api_call pxe_preboot_api[] __pxe_api_call = {
00383         PXE_API_CALL ( PXENV_UNLOAD_STACK, pxenv_unload_stack,
00384                        struct s_PXENV_UNLOAD_STACK ),
00385         PXE_API_CALL ( PXENV_GET_CACHED_INFO, pxenv_get_cached_info,
00386                        struct s_PXENV_GET_CACHED_INFO ),
00387         PXE_API_CALL ( PXENV_RESTART_TFTP, pxenv_restart_tftp,
00388                        struct s_PXENV_TFTP_READ_FILE ),
00389         PXE_API_CALL ( PXENV_START_UNDI, pxenv_start_undi,
00390                        struct s_PXENV_START_UNDI ),
00391         PXE_API_CALL ( PXENV_STOP_UNDI, pxenv_stop_undi,
00392                        struct s_PXENV_STOP_UNDI ),
00393         PXE_API_CALL ( PXENV_START_BASE, pxenv_start_base,
00394                        struct s_PXENV_START_BASE ),
00395         PXE_API_CALL ( PXENV_STOP_BASE, pxenv_stop_base,
00396                        struct s_PXENV_STOP_BASE ),
00397 };