--- /dev/null
+/*
+ * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <ipxe/dhcppkt.h>
+#include <ipxe/init.h>
+#include <ipxe/netdevice.h>
+#include <realmode.h>
+#include <pxe_api.h>
+
+/** @file
+ *
+ * Cached DHCP packet
+ *
+ */
+
+/** Cached DHCPACK physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( cached_dhcpack_phys );
+#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
+
+/** Colour for debug messages */
+#define colour &cached_dhcpack_phys
+
+/** Cached DHCPACK */
+static struct dhcp_packet *cached_dhcpack;
+
+/**
+ * Cached DHCPACK startup function
+ *
+ */
+static void cachedhcp_startup ( void ) {
+ struct dhcp_packet *dhcppkt;
+ struct dhcp_packet *tmp;
+ struct dhcphdr *dhcphdr;
+ size_t len;
+
+ /* Do nothing if no cached DHCPACK is present */
+ if ( ! cached_dhcpack_phys ) {
+ DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
+ return;
+ }
+
+ /* No reliable way to determine length before parsing packet;
+ * start by assuming maximum length permitted by PXE.
+ */
+ len = sizeof ( BOOTPLAYER_t );
+
+ /* Allocate and populate DHCP packet */
+ dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
+ if ( ! dhcppkt ) {
+ DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
+ return;
+ }
+ dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
+ copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
+ len );
+ dhcppkt_init ( dhcppkt, dhcphdr, len );
+
+ /* Resize packet to required length. If reallocation fails,
+ * just continue to use the original packet.
+ */
+ len = dhcppkt_len ( dhcppkt );
+ tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
+ if ( tmp )
+ dhcppkt = tmp;
+
+ /* Reinitialise packet at new address */
+ dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
+ dhcppkt_init ( dhcppkt, dhcphdr, len );
+
+ /* Store as cached DHCPACK, and mark original copy as consumed */
+ DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
+ cached_dhcpack_phys, len );
+ cached_dhcpack = dhcppkt;
+ cached_dhcpack_phys = 0;
+}
+
+/**
+ * Cached DHCPACK shutdown function
+ *
+ * @v booting Shutting down in order to boot
+ */
+static void cachedhcp_shutdown ( int booting __unused ) {
+
+ /* If cached DHCP packet has not yet been claimed, free it */
+ if ( cached_dhcpack ) {
+ DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
+ dhcppkt_put ( cached_dhcpack );
+ cached_dhcpack = NULL;
+ }
+}
+
+/** Cached DHCPACK initialisation function */
+struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+ .startup = cachedhcp_startup,
+ .shutdown = cachedhcp_shutdown,
+};
+
+/**
+ * Apply cached DHCPACK to network device, if applicable
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int cachedhcp_probe ( struct net_device *netdev ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ int rc;
+
+ /* Do nothing unless we have a cached DHCPACK */
+ if ( ! cached_dhcpack )
+ return 0;
+
+ /* Do nothing unless cached DHCPACK's MAC address matches this
+ * network device.
+ */
+ if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
+ ll_protocol->ll_addr_len ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
+ netdev->name );
+ return 0;
+ }
+ DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
+
+ /* Register as DHCP settings for this network device */
+ if ( ( rc = register_settings ( &cached_dhcpack->settings,
+ netdev_settings ( netdev ),
+ DHCP_SETTINGS_NAME ) ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Claim cached DHCPACK */
+ dhcppkt_put ( cached_dhcpack );
+ cached_dhcpack = NULL;
+
+ return 0;
+}
+
+/**
+ * Handle network device link state change
+ *
+ * @v netdev Network device
+ */
+static void cachedhcp_notify ( struct net_device *netdev __unused ) {
+
+ /* Nothing to do */
+}
+
+/**
+ * Handle network device removal
+ *
+ * @v netdev Network device
+ */
+static void cachedhcp_remove ( struct net_device *netdev __unused ) {
+
+ /* Nothing to do */
+}
+
+/** Cached DHCP packet network device driver */
+struct net_driver cachedhcp_driver __net_driver = {
+ .name = "cachedhcp",
+ .probe = cachedhcp_probe,
+ .notify = cachedhcp_notify,
+ .remove = cachedhcp_remove,
+};
+++ /dev/null
-/*
- * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <string.h>
-#include <ipxe/dhcp.h>
-#include <ipxe/netdevice.h>
-#include <undipreload.h>
-#include <pxeparent.h>
-#include <realmode.h>
-#include <pxe_api.h>
-
-/**
- * Present cached DHCP packet if it exists
- */
-void get_cached_dhcpack ( void ) {
- struct undi_device *undi;
- struct s_PXENV_GET_CACHED_INFO get_cached_info;
- int rc;
-
- /* Use preloaded UNDI device to get at PXE entry point */
- undi = &preloaded_undi;
- if ( ! undi->entry.segment ) {
- DBG ( "PXEDHCP no preloaded UNDI device found\n" );
- return;
- }
-
- /* Check that stack is available to get cached info */
- if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
- DBG ( "PXEDHCP stack was unloaded, no cache available\n" );
- return;
- }
-
- /* Obtain cached DHCP packet */
- memset ( &get_cached_info, 0, sizeof ( get_cached_info ) );
- get_cached_info.PacketType = PXENV_PACKET_TYPE_DHCP_ACK;
-
- if ( ( rc = pxeparent_call ( undi->entry, PXENV_GET_CACHED_INFO,
- &get_cached_info,
- sizeof ( get_cached_info ) ) ) != 0 ) {
- DBG ( "PXEDHCP GET_CACHED_INFO failed: %s\n", strerror ( rc ) );
- return;
- }
-
- DBG ( "PXEDHCP got cached info at %04x:%04x length %d\n",
- get_cached_info.Buffer.segment, get_cached_info.Buffer.offset,
- get_cached_info.BufferSize );
-
- /* Present cached DHCP packet */
- store_cached_dhcpack ( real_to_user ( get_cached_info.Buffer.segment,
- get_cached_info.Buffer.offset ),
- get_cached_info.BufferSize );
-}
#define PXENV_UNDI_GET_IFACE_INFO 0x0013
#define PXENV_STOP_UNDI 0x0015
#define PXENV_UNLOAD_STACK 0x0070
+#define PXENV_GET_CACHED_INFO 0x0071
+#define PXENV_PACKET_TYPE_DHCP_ACK 0x0002
#define PXENV_FILE_CMDLINE 0x00e8
#define PXE_HACK_EB54 0x0001
#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
+/* Prefix memory layout:
+ *
+ * iPXE binary image
+ * Temporary stack
+ * Temporary copy of DHCPACK packet
+ * Temporary copy of command line
+ */
#define PREFIX_STACK_SIZE 2048
+#define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE
+#define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ )
+#define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE )
+#define PREFIX_TEMP_CMDLINE_SIZE 4096
/*****************************************************************************
* Entry point: set operating context, print welcome message
99: movb $0x0a, %al
call print_character
+/*****************************************************************************
+ * Get cached DHCP_ACK packet
+ *****************************************************************************
+ */
+get_dhcpack:
+ /* Issue PXENV_GET_CACHED_INFO */
+ xorl %esi, %esi
+ movw %ss, %si
+ movw %si, ( pxe_parameter_structure + 0x08 )
+ movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 )
+ movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 )
+ movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 )
+ movw $PXENV_GET_CACHED_INFO, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Store physical address of packet */
+ shll $4, %esi
+ addl $PREFIX_TEMP_DHCPACK, %esi
+ movl %esi, pxe_cached_dhcpack
+99:
+ .section ".prefix.data", "aw", @progbits
+pxe_cached_dhcpack:
+ .long 0
+ .previous
/*****************************************************************************
* Check for a command line
xorl %esi, %esi
movw %ss, %si
movw %si, ( pxe_parameter_structure + 0x06 )
- movw $PREFIX_STACK_SIZE, ( pxe_parameter_structure + 0x04 )
- movw $0xffff, ( pxe_parameter_structure + 0x02 )
+ movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 )
+ movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 )
movw $PXENV_FILE_CMDLINE, %bx
call pxe_call
jc 99f /* Suppress errors; this is an iPXE extension API call */
jz 99f
/* Record command line */
shll $4, %esi
- addl $PREFIX_STACK_SIZE, %esi
+ addl $PREFIX_TEMP_CMDLINE, %esi
movl %esi, pxe_cmdline
99:
.section ".prefix.data", "aw", @progbits
/* Retrieve PXE command line, if any */
movl pxe_cmdline, %esi
+ /* Retrieve cached DHCPACK, if any */
+ movl pxe_cached_dhcpack, %ecx
+
/* Jump to .text16 segment with %ds pointing to .data16 */
movw %bx, %ds
pushw %ax
/* Store command-line pointer */
movl %esi, cmdline_phys
+ /* Store cached DHCPACK pointer */
+ movl %ecx, cached_dhcpack_phys
+
/* Run main program */
pushl $main
pushw %cs
uint16_t device;
} __attribute__ (( packed ));
-/** Use cached network settings
- *
- * Cached network settings may be available from a prior DHCP request
- * (if running as a PXE NBP), non-volatile storage on the NIC, or
- * settings set via the command line or an embedded image. If this
- * flag is not set, it will be assumed that those sources are
- * insufficient and that DHCP should still be run when autobooting.
- */
+/** Use cached network settings (obsolete; do not reuse this value) */
#define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 )
/** BIOS drive number
extern int start_pxebs ( struct interface *job, struct net_device *netdev,
unsigned int pxe_type );
-/* In environments that can provide cached DHCP packets, this function
- * should look for such a packet and call store_cached_dhcpack() with
- * it if it exists.
- */
-extern void get_cached_dhcpack ( void );
-
-extern void store_cached_dhcpack ( userptr_t data, size_t len );
-
#endif /* _IPXE_DHCP_H */
+++ /dev/null
-/*
- * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ipxe/dhcp.h>
-#include <ipxe/dhcppkt.h>
-#include <ipxe/netdevice.h>
-#include <ipxe/iobuf.h>
-#include <ipxe/uaccess.h>
-
-/** @file
- *
- * Cached DHCP packet handling
- *
- */
-
-/**
- * Store cached DHCPACK packet
- *
- * @v data User pointer to cached DHCP packet data
- * @v len Length of cached DHCP packet data
- * @ret rc Return status code
- *
- * This function should be called by the architecture-specific
- * get_cached_dhcpack() handler.
- */
-void store_cached_dhcpack ( userptr_t data, size_t len ) {
- struct dhcp_packet *dhcppkt;
- struct dhcphdr *dhcphdr;
- struct settings *parent;
- int rc;
-
- /* Create DHCP packet */
- dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
- if ( ! dhcppkt )
- return;
-
- /* Fill in data for DHCP packet */
- dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) );
- copy_from_user ( dhcphdr, data, 0, len );
- dhcppkt_init ( dhcppkt, dhcphdr, len );
- DBG_HD ( dhcppkt->options.data, dhcppkt->options.used_len );
-
- /* Register settings on the last opened network device.
- * This will have the effect of registering cached settings
- * with a network device when "dhcp netX" is performed for that
- * device, which is usually what we want.
- */
- parent = netdev_settings ( last_opened_netdev() );
- if ( ( rc = register_settings ( &dhcppkt->settings, parent,
- DHCP_SETTINGS_NAME ) ) != 0 )
- DBG ( "DHCP could not register cached settings: %s\n",
- strerror ( rc ) );
-
- dhcppkt_put ( dhcppkt );
-
- DBG ( "DHCP registered cached settings\n" );
-}
.type = &setting_type_string,
};
-/** Use cached network settings */
-struct setting use_cached_setting __setting ( SETTING_MISC ) = {
- .name = "use-cached",
- .description = "Use cached settings",
- .tag = DHCP_EB_USE_CACHED,
- .type = &setting_type_uint8,
-};
-
/**
* Most recent DHCP transaction ID
*
.sa_family = AF_INET,
};
-/**
- * Get cached DHCPACK where none exists
- */
-__weak void get_cached_dhcpack ( void ) { __keepme }
-
/**
* Start DHCP state machine on a network device
*
* @v job Job control interface
* @v netdev Network device
- * @ret rc Return status code, or positive if cached
+ * @ret rc Return status code
*
* Starts DHCP on the specified network device. If successful, the
* DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
* option sources.
- *
- * On a return of 0, a background job has been started to perform the
- * DHCP request. Any nonzero return means the job has not been
- * started; a positive return value indicates the success condition of
- * having fetched the appropriate data from cached information.
*/
int start_dhcp ( struct interface *job, struct net_device *netdev ) {
struct dhcp_session *dhcp;
int rc;
- /* Check for cached DHCP information */
- get_cached_dhcpack();
- if ( fetch_uintz_setting ( NULL, &use_cached_setting ) ) {
- DBG ( "DHCP using cached network settings\n" );
- return 1;
- }
-
/* Allocate and initialise structure */
dhcp = zalloc ( sizeof ( *dhcp ) );
if ( ! dhcp )
/* Perform DHCP */
printf ( "DHCP (%s %s)", netdev->name,
netdev->ll_protocol->ntoa ( netdev->ll_addr ) );
- if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 ) {
+ if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
rc = monojob_wait ( "" );
- } else if ( rc > 0 ) {
- printf ( " using cached\n" );
- rc = 0;
- }
return rc;
}