david/ipxe
david
/
ipxe
Archived
1
0
Fork 0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/usr/autoboot.c

622 lines
16 KiB
C
Raw Normal View History

/*
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
2007-01-19 02:13:12 +01:00
#include <stdio.h>
#include <errno.h>
#include <ipxe/netdevice.h>
#include <ipxe/dhcp.h>
#include <ipxe/settings.h>
#include <ipxe/image.h>
#include <ipxe/sanboot.h>
#include <ipxe/uri.h>
#include <ipxe/open.h>
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
#include <ipxe/init.h>
#include <ipxe/keys.h>
#include <ipxe/version.h>
#include <ipxe/shell.h>
#include <ipxe/features.h>
#include <ipxe/image.h>
#include <ipxe/timer.h>
#include <usr/ifmgmt.h>
#include <usr/route.h>
#include <usr/imgmgmt.h>
#include <usr/prompt.h>
#include <usr/autoboot.h>
#include <config/general.h>
#include <config/branding.h>
/** @file
*
* Automatic booting
*
*/
/** Link-layer address of preferred autoboot device, if known */
static uint8_t autoboot_ll_addr[MAX_LL_ADDR_LEN];
/** Device location of preferred autoboot device, if known */
static struct device_description autoboot_desc;
/** Autoboot device tester */
static int ( * is_autoboot_device ) ( struct net_device *netdev );
/* Disambiguate the various error causes */
#define ENOENT_BOOT __einfo_error ( EINFO_ENOENT_BOOT )
#define EINFO_ENOENT_BOOT \
__einfo_uniqify ( EINFO_ENOENT, 0x01, "Nothing to boot" )
#define NORMAL "\033[0m"
#define BOLD "\033[1m"
#define CYAN "\033[36m"
/** The "scriptlet" setting */
const struct setting scriptlet_setting __setting ( SETTING_MISC, scriptlet ) = {
.name = "scriptlet",
.description = "Boot scriptlet",
.tag = DHCP_EB_SCRIPTLET,
.type = &setting_type_string,
};
/**
* Perform PXE menu boot when PXE stack is not available
*/
__weak int pxe_menu_boot ( struct net_device *netdev __unused ) {
return -ENOTSUP;
}
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
/** The "keep-san" setting */
const struct setting keep_san_setting __setting ( SETTING_SANBOOT_EXTRA,
keep-san ) = {
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
.name = "keep-san",
.description = "Preserve SAN connection",
.tag = DHCP_EB_KEEP_SAN,
.type = &setting_type_int8,
};
/** The "skip-san-boot" setting */
const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA,
skip-san-boot ) = {
.name = "skip-san-boot",
.description = "Do not boot from SAN device",
.tag = DHCP_EB_SKIP_SAN_BOOT,
.type = &setting_type_int8,
};
2007-07-08 23:01:49 +02:00
/**
* Boot from filename and root-path URIs
2007-07-08 23:01:49 +02:00
*
* @v filename Filename
* @v root_paths Root path(s)
* @v root_path_count Number of root paths
* @v drive SAN drive (if applicable)
* @v san_filename SAN filename (or NULL to use default)
* @v flags Boot action flags
2007-07-08 23:01:49 +02:00
* @ret rc Return status code
*
* The somewhat tortuous flow of control in this function exists in
* order to ensure that the "sanboot" command remains identical in
* function to a SAN boot via a DHCP-specified root path, and to
* provide backwards compatibility for the "keep-san" and
* "skip-san-boot" options.
2007-07-08 23:01:49 +02:00
*/
int uriboot ( struct uri *filename, struct uri **root_paths,
unsigned int root_path_count, int drive,
const char *san_filename, unsigned int flags ) {
struct image *image;
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
int rc;
/* Hook SAN device, if applicable */
if ( root_path_count ) {
[block] Describe all SAN devices via ACPI tables Describe all SAN devices via ACPI tables such as the iBFT. For tables that can describe only a single device (i.e. the aBFT and sBFT), one table is installed per device. For multi-device tables (i.e. the iBFT), all devices are described in a single table. An underlying SAN device connection may be closed at the time that we need to construct an ACPI table. We therefore introduce the concept of an "ACPI descriptor" which enables the SAN boot code to maintain an opaque pointer to the underlying object, and an "ACPI model" which can build tables from a list of such descriptors. This separates the lifecycles of ACPI descriptions from the lifecycles of the block device interfaces, and allows for construction of the ACPI tables even if the block device interface has been closed. For a multipath SAN device, iPXE will wait until sufficient information is available to describe all devices but will not wait for all paths to connect successfully. For example: with a multipath iSCSI boot iPXE will wait until at least one path has become available and name resolution has completed on all other paths. We do this since the iBFT has to include IP addresses rather than DNS names. We will commence booting without waiting for the inactive paths to either become available or close; this avoids unnecessary boot delays. Note that the Linux kernel will refuse to accept an iBFT with more than two NIC or target structures. We therefore describe only the NICs that are actually required in order to reach the described targets. Any iBFT with at most two targets is therefore guaranteed to describe at most two NICs. Signed-off-by: Michael Brown <mcb30@ipxe.org>
2017-03-27 17:20:34 +02:00
drive = san_hook ( drive, root_paths, root_path_count,
( ( flags & URIBOOT_NO_SAN_DESCRIBE ) ?
SAN_NO_DESCRIBE : 0 ) );
if ( drive < 0 ) {
rc = drive;
printf ( "Could not open SAN device: %s\n",
strerror ( rc ) );
goto err_san_hook;
}
printf ( "Registered SAN device %#02x\n", drive );
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
}
/* Describe SAN device, if applicable */
if ( ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) {
[block] Describe all SAN devices via ACPI tables Describe all SAN devices via ACPI tables such as the iBFT. For tables that can describe only a single device (i.e. the aBFT and sBFT), one table is installed per device. For multi-device tables (i.e. the iBFT), all devices are described in a single table. An underlying SAN device connection may be closed at the time that we need to construct an ACPI table. We therefore introduce the concept of an "ACPI descriptor" which enables the SAN boot code to maintain an opaque pointer to the underlying object, and an "ACPI model" which can build tables from a list of such descriptors. This separates the lifecycles of ACPI descriptions from the lifecycles of the block device interfaces, and allows for construction of the ACPI tables even if the block device interface has been closed. For a multipath SAN device, iPXE will wait until sufficient information is available to describe all devices but will not wait for all paths to connect successfully. For example: with a multipath iSCSI boot iPXE will wait until at least one path has become available and name resolution has completed on all other paths. We do this since the iBFT has to include IP addresses rather than DNS names. We will commence booting without waiting for the inactive paths to either become available or close; this avoids unnecessary boot delays. Note that the Linux kernel will refuse to accept an iBFT with more than two NIC or target structures. We therefore describe only the NICs that are actually required in order to reach the described targets. Any iBFT with at most two targets is therefore guaranteed to describe at most two NICs. Signed-off-by: Michael Brown <mcb30@ipxe.org>
2017-03-27 17:20:34 +02:00
if ( ( rc = san_describe() ) != 0 ) {
printf ( "Could not describe SAN devices: %s\n",
strerror ( rc ) );
goto err_san_describe;
}
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
}
/* Allow a root-path-only boot with skip-san enabled to succeed */
rc = 0;
/* Attempt filename boot if applicable */
if ( filename ) {
if ( ( rc = imgdownload ( filename, 0, &image ) ) != 0 )
goto err_download;
imgstat ( image );
image->flags |= IMAGE_AUTO_UNREGISTER;
if ( ( rc = image_exec ( image ) ) != 0 ) {
printf ( "Could not boot image: %s\n",
strerror ( rc ) );
/* Fall through to (possibly) attempt a SAN boot
* as a fallback. If no SAN boot is attempted,
* our status will become the return status.
*/
} else {
/* Always print an extra newline, because we
* don't know where the NBP may have left the
* cursor.
*/
printf ( "\n" );
}
}
/* Attempt SAN boot if applicable */
if ( ! ( flags & URIBOOT_NO_SAN_BOOT ) ) {
if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) {
printf ( "Booting%s%s from SAN device %#02x\n",
( san_filename ? " " : "" ),
( san_filename ? san_filename : "" ), drive );
rc = san_boot ( drive, san_filename );
printf ( "Boot from SAN device %#02x failed: %s\n",
drive, strerror ( rc ) );
} else {
printf ( "Skipping boot from SAN device %#02x\n",
drive );
/* Avoid overwriting a possible failure status
* from a filename boot.
*/
}
}
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
err_download:
err_san_describe:
/* Unhook SAN device, if applicable */
if ( ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) {
if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) {
san_unhook ( drive );
printf ( "Unregistered SAN device %#02x\n", drive );
} else {
printf ( "Preserving SAN device %#02x\n", drive );
}
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
}
err_san_hook:
[block] Replace gPXE block-device API with an iPXE asynchronous interface The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-09-03 17:11:51 +02:00
return rc;
2007-07-08 23:01:49 +02:00
}
/**
* Close all open net devices
*
* Called before a fresh boot attempt in order to free up memory. We
* don't just close the device immediately after the boot fails,
* because there may still be TCP connections in the process of
* closing.
*/
static void close_all_netdevs ( void ) {
struct net_device *netdev;
for_each_netdev ( netdev ) {
ifclose ( netdev );
}
}
2007-07-08 23:01:49 +02:00
/**
* Fetch next-server and filename settings into a URI
2007-07-08 23:01:49 +02:00
*
* @v settings Settings block
* @ret uri URI, or NULL on failure
2007-07-08 23:01:49 +02:00
*/
struct uri * fetch_next_server_and_filename ( struct settings *settings ) {
union {
struct sockaddr sa;
struct sockaddr_in sin;
} next_server;
char *raw_filename = NULL;
struct uri *uri = NULL;
char *filename;
/* Initialise server address */
memset ( &next_server, 0, sizeof ( next_server ) );
/* If we have a filename, fetch it along with the next-server
* setting from the same settings block.
*/
if ( fetch_setting ( settings, &filename_setting, &settings,
NULL, NULL, 0 ) >= 0 ) {
fetch_string_setting_copy ( settings, &filename_setting,
&raw_filename );
fetch_ipv4_setting ( settings, &next_server_setting,
&next_server.sin.sin_addr );
}
if ( ! raw_filename )
goto err_fetch;
/* Populate server address */
if ( next_server.sin.sin_addr.s_addr ) {
next_server.sin.sin_family = AF_INET;
printf ( "Next server: %s\n",
inet_ntoa ( next_server.sin.sin_addr ) );
}
/* Expand filename setting */
filename = expand_settings ( raw_filename );
if ( ! filename )
goto err_expand;
if ( filename[0] )
printf ( "Filename: %s\n", filename );
/* Construct URI */
uri = pxe_uri ( &next_server.sa, filename );
if ( ! uri )
goto err_parse;
err_parse:
free ( filename );
err_expand:
free ( raw_filename );
err_fetch:
return uri;
}
/**
* Fetch root-path setting into a URI
*
* @v settings Settings block
* @ret uri URI, or NULL on failure
*/
static struct uri * fetch_root_path ( struct settings *settings ) {
struct uri *uri = NULL;
char *raw_root_path;
char *root_path;
/* Fetch root-path setting */
fetch_string_setting_copy ( settings, &root_path_setting,
&raw_root_path );
if ( ! raw_root_path )
goto err_fetch;
/* Expand filename setting */
root_path = expand_settings ( raw_root_path );
if ( ! root_path )
goto err_expand;
if ( root_path[0] )
printf ( "Root path: %s\n", root_path );
/* Parse root path */
uri = parse_uri ( root_path );
if ( ! uri )
goto err_parse;
err_parse:
free ( root_path );
err_expand:
free ( raw_root_path );
err_fetch:
return uri;
}
/**
* Fetch san-filename setting
*
* @v settings Settings block
* @ret san_filename SAN filename, or NULL on failure
*/
static char * fetch_san_filename ( struct settings *settings ) {
char *raw_san_filename;
char *san_filename = NULL;
/* Fetch san-filename setting */
fetch_string_setting_copy ( settings, &san_filename_setting,
&raw_san_filename );
if ( ! raw_san_filename )
goto err_fetch;
/* Expand san-filename setting */
san_filename = expand_settings ( raw_san_filename );
if ( ! san_filename )
goto err_expand;
if ( san_filename[0] )
printf ( "SAN filename: %s\n", san_filename );
err_expand:
free ( raw_san_filename );
err_fetch:
return san_filename;
}
/**
* Check whether or not we have a usable PXE menu
*
* @ret have_menu A usable PXE menu is present
*/
static int have_pxe_menu ( void ) {
struct setting vendor_class_id_setting
= { .tag = DHCP_VENDOR_CLASS_ID };
struct setting pxe_discovery_control_setting
= { .tag = DHCP_PXE_DISCOVERY_CONTROL };
struct setting pxe_boot_menu_setting
= { .tag = DHCP_PXE_BOOT_MENU };
char buf[ 10 /* "PXEClient" + NUL */ ];
unsigned int pxe_discovery_control;
fetch_string_setting ( NULL, &vendor_class_id_setting,
buf, sizeof ( buf ) );
pxe_discovery_control =
fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
return ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
setting_exists ( NULL, &pxe_boot_menu_setting ) &&
( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
setting_exists ( NULL, &filename_setting ) ) ) );
}
/**
* Boot from a network device
*
* @v netdev Network device
* @ret rc Return status code
*/
int netboot ( struct net_device *netdev ) {
struct uri *filename;
struct uri *root_path;
char *san_filename;
2007-07-08 23:01:49 +02:00
int rc;
/* Close all other network devices */
close_all_netdevs();
2007-07-08 23:01:49 +02:00
/* Open device and display device status */
if ( ( rc = ifopen ( netdev ) ) != 0 )
goto err_ifopen;
2007-07-08 23:01:49 +02:00
ifstat ( netdev );
/* Configure device */
if ( ( rc = ifconf ( netdev, NULL ) ) != 0 )
goto err_dhcp;
2007-07-08 23:01:49 +02:00
route();
/* Try PXE menu boot, if applicable */
if ( have_pxe_menu() ) {
printf ( "Booting from PXE menu\n" );
rc = pxe_menu_boot ( netdev );
goto err_pxe_menu_boot;
}
/* Fetch next server and filename (if any) */
filename = fetch_next_server_and_filename ( NULL );
/* Fetch root path (if any) */
root_path = fetch_root_path ( NULL );
/* Fetch SAN filename (if any) */
san_filename = fetch_san_filename ( NULL );
/* If we have both a filename and a root path, ignore an
* unsupported or missing URI scheme in the root path, since
* it may represent an NFS root.
*/
if ( filename && root_path &&
( ( ! uri_is_absolute ( root_path ) ) ||
( xfer_uri_opener ( root_path->scheme ) == NULL ) ) ) {
printf ( "Ignoring unsupported root path\n" );
uri_put ( root_path );
root_path = NULL;
}
/* Check that we have something to boot */
if ( ! ( filename || root_path ) ) {
rc = -ENOENT_BOOT;
printf ( "Nothing to boot: %s\n", strerror ( rc ) );
goto err_no_boot;
}
/* Boot using next server, filename and root path */
if ( ( rc = uriboot ( filename, &root_path, ( root_path ? 1 : 0 ),
san_default_drive(), san_filename,
( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 )
goto err_uriboot;
err_uriboot:
err_no_boot:
free ( san_filename );
uri_put ( root_path );
uri_put ( filename );
err_pxe_menu_boot:
err_dhcp:
err_ifopen:
return rc;
}
/**
* Test if network device matches the autoboot device bus type and location
*
* @v netdev Network device
* @ret is_autoboot Network device matches the autoboot device
*/
static int is_autoboot_busloc ( struct net_device *netdev ) {
struct device *dev;
for ( dev = netdev->dev ; dev ; dev = dev->parent ) {
if ( ( dev->desc.bus_type == autoboot_desc.bus_type ) &&
( dev->desc.location == autoboot_desc.location ) )
return 1;
}
return 0;
}
/**
* Identify autoboot device by bus type and location
*
* @v bus_type Bus type
* @v location Location
*/
void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ) {
/* Record autoboot device description */
autoboot_desc.bus_type = bus_type;
autoboot_desc.location = location;
/* Mark autoboot device as present */
is_autoboot_device = is_autoboot_busloc;
}
/**
* Test if network device matches the autoboot device link-layer address
*
* @v netdev Network device
* @ret is_autoboot Network device matches the autoboot device
*/
static int is_autoboot_ll_addr ( struct net_device *netdev ) {
return ( memcmp ( netdev->ll_addr, autoboot_ll_addr,
netdev->ll_protocol->ll_addr_len ) == 0 );
}
/**
* Identify autoboot device by link-layer address
*
* @v ll_addr Link-layer address
* @v len Length of link-layer address
*/
void set_autoboot_ll_addr ( const void *ll_addr, size_t len ) {
/* Record autoboot link-layer address (truncated if necessary) */
if ( len > sizeof ( autoboot_ll_addr ) )
len = sizeof ( autoboot_ll_addr );
memcpy ( autoboot_ll_addr, ll_addr, len );
/* Mark autoboot device as present */
is_autoboot_device = is_autoboot_ll_addr;
}
/**
* Boot the system
*/
static int autoboot ( void ) {
struct net_device *netdev;
int rc = -ENODEV;
/* Try booting from each network device. If we have a
* specified autoboot device location, then use only devices
* matching that location.
*/
for_each_netdev ( netdev ) {
/* Skip any non-matching devices, if applicable */
if ( is_autoboot_device && ( ! is_autoboot_device ( netdev ) ) )
continue;
/* Attempt booting from this device */
rc = netboot ( netdev );
}
printf ( "No more network devices\n" );
return rc;
}
/**
* Prompt for shell entry
*
* @ret enter_shell User wants to enter shell
*/
static int shell_banner ( void ) {
/* Skip prompt if timeout is zero */
if ( BANNER_TIMEOUT <= 0 )
return 0;
/* Prompt user */
printf ( "\n" );
return ( prompt ( "Press Ctrl-B for the " PRODUCT_SHORT_NAME
" command line...",
( ( BANNER_TIMEOUT * TICKS_PER_SEC ) / 10 ),
CTRL_B ) == 0 );
}
/**
* Main iPXE flow of execution
*
* @v netdev Network device, or NULL
* @ret rc Return status code
*/
int ipxe ( struct net_device *netdev ) {
struct feature *feature;
struct image *image;
char *scriptlet;
int rc;
/*
* Print welcome banner
*
*
* If you wish to brand this build of iPXE, please do so by
* defining the string PRODUCT_NAME in config/branding.h.
*
* While nothing in the GPL prevents you from removing all
* references to iPXE or http://ipxe.org, we prefer you not to
* do so.
*
*/
printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD PRODUCT_SHORT_NAME " %s"
NORMAL " -- " PRODUCT_TAG_LINE " -- "
CYAN PRODUCT_URI NORMAL "\nFeatures:", product_version );
for_each_table_entry ( feature, FEATURES )
printf ( " %s", feature->name );
printf ( "\n" );
/* Boot system */
if ( ( image = first_image() ) != NULL ) {
/* We have an embedded image; execute it */
return image_exec ( image );
} else if ( shell_banner() ) {
/* User wants shell; just give them a shell */
return shell();
} else {
fetch_string_setting_copy ( NULL, &scriptlet_setting,
&scriptlet );
if ( scriptlet ) {
/* User has defined a scriptlet; execute it */
rc = system ( scriptlet );
free ( scriptlet );
return rc;
} else {
/* Try booting. If booting fails, offer the
* user another chance to enter the shell.
*/
if ( netdev ) {
rc = netboot ( netdev );
} else {
rc = autoboot();
}
if ( shell_banner() )
rc = shell();
return rc;
}
}
}