summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/usr
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/usr')
-rw-r--r--qemu/roms/ipxe/src/usr/autoboot.c587
-rw-r--r--qemu/roms/ipxe/src/usr/dhcpmgmt.c46
-rw-r--r--qemu/roms/ipxe/src/usr/fcmgmt.c116
-rw-r--r--qemu/roms/ipxe/src/usr/ifmgmt.c289
-rw-r--r--qemu/roms/ipxe/src/usr/imgmgmt.c166
-rw-r--r--qemu/roms/ipxe/src/usr/imgtrust.c110
-rw-r--r--qemu/roms/ipxe/src/usr/ipstat.c62
-rw-r--r--qemu/roms/ipxe/src/usr/iwmgmt.c226
-rw-r--r--qemu/roms/ipxe/src/usr/lotest.c275
-rw-r--r--qemu/roms/ipxe/src/usr/neighmgmt.c56
-rw-r--r--qemu/roms/ipxe/src/usr/nslookup.c203
-rw-r--r--qemu/roms/ipxe/src/usr/pingmgmt.c84
-rw-r--r--qemu/roms/ipxe/src/usr/profstat.c44
-rw-r--r--qemu/roms/ipxe/src/usr/prompt.c66
-rw-r--r--qemu/roms/ipxe/src/usr/pxemenu.c380
-rw-r--r--qemu/roms/ipxe/src/usr/route.c44
-rw-r--r--qemu/roms/ipxe/src/usr/route_ipv4.c58
-rw-r--r--qemu/roms/ipxe/src/usr/route_ipv6.c60
-rw-r--r--qemu/roms/ipxe/src/usr/sync.c74
19 files changed, 2946 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/usr/autoboot.c b/qemu/roms/ipxe/src/usr/autoboot.c
new file mode 100644
index 000000000..47476ae40
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/autoboot.c
@@ -0,0 +1,587 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#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>
+#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>
+
+/** @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;
+}
+
+/**
+ * Parse next-server and filename into a URI
+ *
+ * @v next_server Next-server address
+ * @v filename Filename
+ * @ret uri URI, or NULL on failure
+ */
+static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
+ const char *filename ) {
+ struct uri *uri;
+
+ /* Parse filename */
+ uri = parse_uri ( filename );
+ if ( ! uri )
+ return NULL;
+
+ /* Construct a TFTP URI for the filename, if applicable */
+ if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) {
+ uri_put ( uri );
+ uri = tftp_uri ( next_server, filename );
+ if ( ! uri )
+ return NULL;
+ }
+
+ return uri;
+}
+
+/** The "keep-san" setting */
+const struct setting keep_san_setting __setting ( SETTING_SANBOOT_EXTRA,
+ keep-san ) = {
+ .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,
+};
+
+/**
+ * Boot from filename and root-path URIs
+ *
+ * @v filename Filename
+ * @v root_path Root path
+ * @v drive SAN drive (if applicable)
+ * @v flags Boot action flags
+ * @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.
+ */
+int uriboot ( struct uri *filename, struct uri *root_path, int drive,
+ unsigned int flags ) {
+ struct image *image;
+ int rc;
+
+ /* Hook SAN device, if applicable */
+ if ( root_path ) {
+ if ( ( rc = san_hook ( root_path, drive ) ) != 0 ) {
+ printf ( "Could not open SAN device: %s\n",
+ strerror ( rc ) );
+ goto err_san_hook;
+ }
+ printf ( "Registered SAN device %#02x\n", drive );
+ }
+
+ /* Describe SAN device, if applicable */
+ if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) {
+ if ( ( rc = san_describe ( drive ) ) != 0 ) {
+ printf ( "Could not describe SAN device %#02x: %s\n",
+ drive, strerror ( rc ) );
+ goto err_san_describe;
+ }
+ }
+
+ /* 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;
+ 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 ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_BOOT ) ) {
+ if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) {
+ printf ( "Booting from SAN device %#02x\n", drive );
+ rc = san_boot ( drive );
+ 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.
+ */
+ }
+ }
+
+ err_download:
+ err_san_describe:
+ /* Unhook SAN device, if applicable */
+ if ( ( drive >= 0 ) && ! ( 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 );
+ }
+ }
+ err_san_hook:
+ return rc;
+}
+
+/**
+ * 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 );
+ }
+}
+
+/**
+ * Fetch next-server and filename settings into a URI
+ *
+ * @v settings Settings block
+ * @ret uri URI, or NULL on failure
+ */
+struct uri * fetch_next_server_and_filename ( struct settings *settings ) {
+ struct in_addr next_server = { 0 };
+ char *raw_filename = NULL;
+ struct uri *uri = NULL;
+ char *filename;
+
+ /* 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 );
+ }
+
+ /* Expand filename setting */
+ filename = expand_settings ( raw_filename ? raw_filename : "" );
+ if ( ! filename )
+ goto err_expand;
+
+ /* Parse next server and filename */
+ if ( next_server.s_addr )
+ printf ( "Next server: %s\n", inet_ntoa ( next_server ) );
+ if ( filename[0] )
+ printf ( "Filename: %s\n", filename );
+ uri = parse_next_server_and_filename ( next_server, filename );
+ if ( ! uri )
+ goto err_parse;
+
+ err_parse:
+ free ( filename );
+ err_expand:
+ free ( raw_filename );
+ 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 );
+
+ /* Expand filename setting */
+ root_path = expand_settings ( raw_root_path ? raw_root_path : "" );
+ if ( ! root_path )
+ goto err_expand;
+
+ /* Parse root path */
+ if ( root_path[0] )
+ printf ( "Root path: %s\n", root_path );
+ uri = parse_uri ( root_path );
+ if ( ! uri )
+ goto err_parse;
+
+ err_parse:
+ free ( root_path );
+ err_expand:
+ free ( raw_root_path );
+ return uri;
+}
+
+/**
+ * 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;
+ int rc;
+
+ /* Close all other network devices */
+ close_all_netdevs();
+
+ /* Open device and display device status */
+ if ( ( rc = ifopen ( netdev ) ) != 0 )
+ goto err_ifopen;
+ ifstat ( netdev );
+
+ /* Configure device */
+ if ( ( rc = ifconf ( netdev, NULL ) ) != 0 )
+ goto err_dhcp;
+ 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 */
+ filename = fetch_next_server_and_filename ( NULL );
+ if ( ! filename )
+ goto err_filename;
+ if ( ! uri_has_path ( filename ) ) {
+ /* Ignore empty filename */
+ uri_put ( filename );
+ filename = NULL;
+ }
+
+ /* Fetch root path */
+ root_path = fetch_root_path ( NULL );
+ if ( ! root_path )
+ goto err_root_path;
+ if ( ! uri_is_absolute ( root_path ) ) {
+ /* Ignore empty root path */
+ uri_put ( root_path );
+ root_path = NULL;
+ }
+
+ /* If we have both a filename and a root path, ignore an
+ * unsupported URI scheme in the root path, since it may
+ * represent an NFS root.
+ */
+ if ( filename && 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, san_default_drive(),
+ ( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 )
+ goto err_uriboot;
+
+ err_uriboot:
+ err_no_boot:
+ uri_put ( root_path );
+ err_root_path:
+ uri_put ( filename );
+ err_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 ) {
+
+ return ( ( netdev->dev->desc.bus_type == autoboot_desc.bus_type ) &&
+ ( netdev->dev->desc.location == autoboot_desc.location ) );
+}
+
+/**
+ * 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 iPXE command line...",
+ ( ( BANNER_TIMEOUT * TICKS_PER_SEC ) / 10 ),
+ CTRL_B ) == 0 );
+}
+
+/**
+ * Main iPXE flow of execution
+ *
+ * @v netdev Network device, or NULL
+ */
+void ipxe ( struct net_device *netdev ) {
+ struct feature *feature;
+ struct image *image;
+ char *scriptlet;
+
+ /*
+ * Print welcome banner
+ *
+ *
+ * If you wish to brand this build of iPXE, please do so by
+ * defining the string PRODUCT_NAME in config/general.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%s\n" BOLD "iPXE %s"
+ NORMAL " -- Open Source Network Boot Firmware -- "
+ CYAN "http://ipxe.org" NORMAL "\n"
+ "Features:", product_name, 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 */
+ image_exec ( image );
+ } else if ( shell_banner() ) {
+ /* User wants shell; just give them a shell */
+ shell();
+ } else {
+ fetch_string_setting_copy ( NULL, &scriptlet_setting,
+ &scriptlet );
+ if ( scriptlet ) {
+ /* User has defined a scriptlet; execute it */
+ system ( scriptlet );
+ free ( scriptlet );
+ } else {
+ /* Try booting. If booting fails, offer the
+ * user another chance to enter the shell.
+ */
+ if ( netdev ) {
+ netboot ( netdev );
+ } else {
+ autoboot();
+ }
+ if ( shell_banner() )
+ shell();
+ }
+ }
+}
diff --git a/qemu/roms/ipxe/src/usr/dhcpmgmt.c b/qemu/roms/ipxe/src/usr/dhcpmgmt.c
new file mode 100644
index 000000000..23982b19c
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/dhcpmgmt.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/monojob.h>
+#include <usr/ifmgmt.h>
+#include <usr/dhcpmgmt.h>
+
+/** @file
+ *
+ * DHCP management
+ *
+ */
+
+int pxebs ( struct net_device *netdev, unsigned int pxe_type ) {
+ int rc;
+
+ /* Perform PXE Boot Server Discovery */
+ printf ( "PXEBS (%s type %d)", netdev->name, pxe_type );
+ if ( ( rc = start_pxebs ( &monojob, netdev, pxe_type ) ) == 0 )
+ rc = monojob_wait ( "", 0 );
+
+ return rc;
+}
diff --git a/qemu/roms/ipxe/src/usr/fcmgmt.c b/qemu/roms/ipxe/src/usr/fcmgmt.c
new file mode 100644
index 000000000..a30f37a71
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/fcmgmt.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/fc.h>
+#include <ipxe/fcels.h>
+#include <ipxe/monojob.h>
+#include <usr/fcmgmt.h>
+
+/** @file
+ *
+ * Fibre Channel management
+ *
+ */
+
+/**
+ * Print status of Fibre Channel port
+ *
+ * @v port Fibre Channel port
+ */
+void fcportstat ( struct fc_port *port ) {
+ printf ( "%s: %s id %s", port->name, fc_ntoa ( &port->port_wwn ),
+ fc_id_ntoa ( &port->port_id ) );
+ printf ( " node %s\n [Link:", fc_ntoa ( &port->node_wwn ) );
+ if ( fc_link_ok ( &port->link ) ) {
+ printf ( " up, %s", fc_ntoa ( &port->link_port_wwn ) );
+ if ( ( port->flags & FC_PORT_HAS_FABRIC ) ) {
+ printf ( " fabric" );
+ } else {
+ printf ( " id %s",
+ fc_id_ntoa ( &port->ptp_link_port_id ) );
+ }
+ printf ( " node %s]\n", fc_ntoa ( &port->link_node_wwn ) );
+ } else {
+ printf ( " down: %s]\n", strerror ( port->link.rc ) );
+ }
+}
+
+/**
+ * Print status of Fibre Channel peer
+ *
+ * @v peer Fibre Channel peer
+ */
+void fcpeerstat ( struct fc_peer *peer ) {
+ struct fc_ulp *ulp;
+ uint8_t *param;
+ unsigned int i;
+
+ printf ( "%s:\n [Link:", fc_ntoa ( &peer->port_wwn ) );
+ if ( fc_link_ok ( &peer->link ) ) {
+ printf ( " up, port %s id %s]\n", peer->port->name,
+ fc_id_ntoa ( &peer->port_id ) );
+ } else {
+ printf ( " down: %s]\n", strerror ( peer->link.rc ) );
+ }
+
+ list_for_each_entry ( ulp, &peer->ulps, list ) {
+ printf ( " [Type %02x link:", ulp->type );
+ if ( fc_link_ok ( &ulp->link ) ) {
+ printf ( " up, params" );
+ param = ulp->param;
+ for ( i = 0 ; i < ulp->param_len ; i++ ) {
+ printf ( "%c%02x", ( ( i == 0 ) ? ' ' : ':' ),
+ param[i] );
+ }
+ } else {
+ printf ( " down: %s", strerror ( ulp->link.rc ) );
+ }
+ printf ( "]\n" );
+ }
+}
+
+/**
+ * Issue Fibre Channel ELS
+ *
+ * @v port Fibre Channel port
+ * @v peer_port_id Peer port ID
+ * @v handler ELS handler
+ * @ret rc Return status code
+ */
+int fcels ( struct fc_port *port, struct fc_port_id *peer_port_id,
+ struct fc_els_handler *handler ) {
+ int rc;
+
+ /* Initiate ELS */
+ printf ( "%s %s to %s...",
+ port->name, handler->name, fc_id_ntoa ( peer_port_id ) );
+ if ( ( rc = fc_els_request ( &monojob, port, peer_port_id,
+ handler ) ) != 0 ) {
+ printf ( "%s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Wait for ELS to complete */
+ return monojob_wait ( "", 0 );
+}
diff --git a/qemu/roms/ipxe/src/usr/ifmgmt.c b/qemu/roms/ipxe/src/usr/ifmgmt.c
new file mode 100644
index 000000000..3d05895c2
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/ifmgmt.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ipxe/console.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/device.h>
+#include <ipxe/job.h>
+#include <ipxe/monojob.h>
+#include <ipxe/timer.h>
+#include <usr/ifmgmt.h>
+
+/** @file
+ *
+ * Network interface management
+ *
+ */
+
+/** Default time to wait for link-up */
+#define LINK_WAIT_TIMEOUT ( 15 * TICKS_PER_SEC )
+
+/** Default unsuccessful configuration status code */
+#define EADDRNOTAVAIL_CONFIG __einfo_error ( EINFO_EADDRNOTAVAIL_CONFIG )
+#define EINFO_EADDRNOTAVAIL_CONFIG \
+ __einfo_uniqify ( EINFO_EADDRNOTAVAIL, 0x01, \
+ "No configuration methods succeeded" )
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+int ifopen ( struct net_device *netdev ) {
+ int rc;
+
+ if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
+ printf ( "Could not open %s: %s\n",
+ netdev->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+void ifclose ( struct net_device *netdev ) {
+ netdev_close ( netdev );
+}
+
+/**
+ * Print network device error breakdown
+ *
+ * @v stats Network device statistics
+ * @v prefix Message prefix
+ */
+static void ifstat_errors ( struct net_device_stats *stats,
+ const char *prefix ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( stats->errors ) /
+ sizeof ( stats->errors[0] ) ) ; i++ ) {
+ if ( stats->errors[i].count )
+ printf ( " [%s: %d x \"%s\"]\n", prefix,
+ stats->errors[i].count,
+ strerror ( stats->errors[i].rc ) );
+ }
+}
+
+/**
+ * Print status of network device
+ *
+ * @v netdev Network device
+ */
+void ifstat ( struct net_device *netdev ) {
+ printf ( "%s: %s using %s on %s (%s)\n"
+ " [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n",
+ netdev->name, netdev_addr ( netdev ),
+ netdev->dev->driver_name, netdev->dev->name,
+ ( netdev_is_open ( netdev ) ? "open" : "closed" ),
+ ( netdev_link_ok ( netdev ) ? "up" : "down" ),
+ netdev->tx_stats.good, netdev->tx_stats.bad,
+ netdev->rx_stats.good, netdev->rx_stats.bad );
+ if ( ! netdev_link_ok ( netdev ) ) {
+ printf ( " [Link status: %s]\n",
+ strerror ( netdev->link_rc ) );
+ }
+ ifstat_errors ( &netdev->tx_stats, "TXE" );
+ ifstat_errors ( &netdev->rx_stats, "RXE" );
+}
+
+/** Network device poller */
+struct ifpoller {
+ /** Job control interface */
+ struct interface job;
+ /** Network device */
+ struct net_device *netdev;
+ /** Network device configurator (if applicable) */
+ struct net_device_configurator *configurator;
+ /**
+ * Check progress
+ *
+ * @v ifpoller Network device poller
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+ int ( * progress ) ( struct ifpoller *ifpoller );
+};
+
+/**
+ * Report network device poller progress
+ *
+ * @v ifpoller Network device poller
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int ifpoller_progress ( struct ifpoller *ifpoller,
+ struct job_progress *progress __unused ) {
+
+ /* Hand off to current progress checker */
+ return ifpoller->progress ( ifpoller );
+}
+
+/** Network device poller operations */
+static struct interface_operation ifpoller_job_op[] = {
+ INTF_OP ( job_progress, struct ifpoller *, ifpoller_progress ),
+};
+
+/** Network device poller descriptor */
+static struct interface_descriptor ifpoller_job_desc =
+ INTF_DESC ( struct ifpoller, job, ifpoller_job_op );
+
+/**
+ * Poll network device until completion
+ *
+ * @v netdev Network device
+ * @v configurator Network device configurator (if applicable)
+ * @v timeout Timeout period, in ticks
+ * @v progress Method to check progress
+ * @ret rc Return status code
+ */
+static int ifpoller_wait ( struct net_device *netdev,
+ struct net_device_configurator *configurator,
+ unsigned long timeout,
+ int ( * progress ) ( struct ifpoller *ifpoller ) ) {
+ static struct ifpoller ifpoller = {
+ .job = INTF_INIT ( ifpoller_job_desc ),
+ };
+
+ ifpoller.netdev = netdev;
+ ifpoller.configurator = configurator;
+ ifpoller.progress = progress;
+ intf_plug_plug ( &monojob, &ifpoller.job );
+ return monojob_wait ( "", timeout );
+}
+
+/**
+ * Check link-up progress
+ *
+ * @v ifpoller Network device poller
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int iflinkwait_progress ( struct ifpoller *ifpoller ) {
+ struct net_device *netdev = ifpoller->netdev;
+ int ongoing_rc = netdev->link_rc;
+
+ /* Terminate successfully if link is up */
+ if ( ongoing_rc == 0 )
+ intf_close ( &ifpoller->job, 0 );
+
+ /* Otherwise, report link status as ongoing job status */
+ return ongoing_rc;
+}
+
+/**
+ * Wait for link-up, with status indication
+ *
+ * @v netdev Network device
+ * @v timeout Timeout period, in ticks
+ */
+int iflinkwait ( struct net_device *netdev, unsigned long timeout ) {
+ int rc;
+
+ /* Ensure device is open */
+ if ( ( rc = ifopen ( netdev ) ) != 0 )
+ return rc;
+
+ /* Return immediately if link is already up */
+ netdev_poll ( netdev );
+ if ( netdev_link_ok ( netdev ) )
+ return 0;
+
+ /* Wait for link-up */
+ printf ( "Waiting for link-up on %s", netdev->name );
+ return ifpoller_wait ( netdev, NULL, timeout, iflinkwait_progress );
+}
+
+/**
+ * Check configuration progress
+ *
+ * @v ifpoller Network device poller
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int ifconf_progress ( struct ifpoller *ifpoller ) {
+ struct net_device *netdev = ifpoller->netdev;
+ struct net_device_configurator *configurator = ifpoller->configurator;
+ struct net_device_configuration *config;
+ int rc;
+
+ /* Do nothing unless configuration has completed */
+ if ( netdev_configuration_in_progress ( netdev ) )
+ return 0;
+
+ /* Terminate with appropriate overall return status code */
+ if ( configurator ) {
+ config = netdev_configuration ( netdev, configurator );
+ rc = config->rc;
+ } else {
+ rc = ( netdev_configuration_ok ( netdev ) ?
+ 0 : -EADDRNOTAVAIL_CONFIG );
+ }
+ intf_close ( &ifpoller->job, rc );
+
+ return rc;
+}
+
+/**
+ * Perform network device configuration
+ *
+ * @v netdev Network device
+ * @v configurator Network device configurator, or NULL to use all
+ * @ret rc Return status code
+ */
+int ifconf ( struct net_device *netdev,
+ struct net_device_configurator *configurator ) {
+ int rc;
+
+ /* Ensure device is open and link is up */
+ if ( ( rc = iflinkwait ( netdev, LINK_WAIT_TIMEOUT ) ) != 0 )
+ return rc;
+
+ /* Start configuration */
+ if ( configurator ) {
+ if ( ( rc = netdev_configure ( netdev, configurator ) ) != 0 ) {
+ printf ( "Could not configure %s via %s: %s\n",
+ netdev->name, configurator->name,
+ strerror ( rc ) );
+ return rc;
+ }
+ } else {
+ if ( ( rc = netdev_configure_all ( netdev ) ) != 0 ) {
+ printf ( "Could not configure %s: %s\n",
+ netdev->name, strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ /* Wait for configuration to complete */
+ printf ( "Configuring %s%s%s(%s %s)",
+ ( configurator ? "[" : "" ),
+ ( configurator ? configurator->name : "" ),
+ ( configurator ? "] " : "" ),
+ netdev->name, netdev->ll_protocol->ntoa ( netdev->ll_addr ) );
+ return ifpoller_wait ( netdev, configurator, 0, ifconf_progress );
+}
diff --git a/qemu/roms/ipxe/src/usr/imgmgmt.c b/qemu/roms/ipxe/src/usr/imgmgmt.c
new file mode 100644
index 000000000..c9c571640
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/imgmgmt.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/image.h>
+#include <ipxe/downloader.h>
+#include <ipxe/monojob.h>
+#include <ipxe/open.h>
+#include <ipxe/uri.h>
+#include <usr/imgmgmt.h>
+
+/** @file
+ *
+ * Image management
+ *
+ */
+
+/**
+ * Download a new image
+ *
+ * @v uri URI
+ * @v timeout Download timeout
+ * @v image Image to fill in
+ * @ret rc Return status code
+ */
+int imgdownload ( struct uri *uri, unsigned long timeout,
+ struct image **image ) {
+ const char *password;
+ char *uri_string_redacted;
+ int rc;
+
+ /* Construct redacted URI */
+ password = uri->password;
+ if ( password )
+ uri->password = "***";
+ uri_string_redacted = format_uri_alloc ( uri );
+ uri->password = password;
+ if ( ! uri_string_redacted ) {
+ rc = -ENOMEM;
+ goto err_uri_string;
+ }
+
+ /* Resolve URI */
+ uri = resolve_uri ( cwuri, uri );
+ if ( ! uri ) {
+ rc = -ENOMEM;
+ goto err_resolve_uri;
+ }
+
+ /* Allocate image */
+ *image = alloc_image ( uri );
+ if ( ! *image ) {
+ rc = -ENOMEM;
+ goto err_alloc_image;
+ }
+
+ /* Create downloader */
+ if ( ( rc = create_downloader ( &monojob, *image ) ) != 0 ) {
+ printf ( "Could not start download: %s\n", strerror ( rc ) );
+ goto err_create_downloader;
+ }
+
+ /* Wait for download to complete */
+ if ( ( rc = monojob_wait ( uri_string_redacted, timeout ) ) != 0 )
+ goto err_monojob_wait;
+
+ /* Register image */
+ if ( ( rc = register_image ( *image ) ) != 0 ) {
+ printf ( "Could not register image: %s\n", strerror ( rc ) );
+ goto err_register_image;
+ }
+
+ err_register_image:
+ err_monojob_wait:
+ err_create_downloader:
+ image_put ( *image );
+ err_alloc_image:
+ uri_put ( uri );
+ err_resolve_uri:
+ free ( uri_string_redacted );
+ err_uri_string:
+ return rc;
+}
+
+/**
+ * Download a new image
+ *
+ * @v uri_string URI string
+ * @v timeout Download timeout
+ * @v image Image to fill in
+ * @ret rc Return status code
+ */
+int imgdownload_string ( const char *uri_string, unsigned long timeout,
+ struct image **image ) {
+ struct uri *uri;
+ int rc;
+
+ if ( ! ( uri = parse_uri ( uri_string ) ) )
+ return -ENOMEM;
+
+ rc = imgdownload ( uri, timeout, image );
+
+ uri_put ( uri );
+ return rc;
+}
+
+/**
+ * Acquire an image
+ *
+ * @v name_uri Name or URI string
+ * @v timeout Download timeout
+ * @v image Image to fill in
+ * @ret rc Return status code
+ */
+int imgacquire ( const char *name_uri, unsigned long timeout,
+ struct image **image ) {
+
+ /* If we already have an image with the specified name, use it */
+ *image = find_image ( name_uri );
+ if ( *image )
+ return 0;
+
+ /* Otherwise, download a new image */
+ return imgdownload_string ( name_uri, timeout, image );
+}
+
+/**
+ * Display status of an image
+ *
+ * @v image Executable/loadable image
+ */
+void imgstat ( struct image *image ) {
+ printf ( "%s : %zd bytes", image->name, image->len );
+ if ( image->type )
+ printf ( " [%s]", image->type->name );
+ if ( image->flags & IMAGE_TRUSTED )
+ printf ( " [TRUSTED]" );
+ if ( image->flags & IMAGE_SELECTED )
+ printf ( " [SELECTED]" );
+ if ( image->flags & IMAGE_AUTO_UNREGISTER )
+ printf ( " [AUTOFREE]" );
+ if ( image->cmdline )
+ printf ( " \"%s\"", image->cmdline );
+ printf ( "\n" );
+}
diff --git a/qemu/roms/ipxe/src/usr/imgtrust.c b/qemu/roms/ipxe/src/usr/imgtrust.c
new file mode 100644
index 000000000..da7ff2ef0
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/imgtrust.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <syslog.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/cms.h>
+#include <ipxe/validator.h>
+#include <ipxe/monojob.h>
+#include <usr/imgtrust.h>
+
+/** @file
+ *
+ * Image trust management
+ *
+ */
+
+/**
+ * Verify image using downloaded signature
+ *
+ * @v image Image to verify
+ * @v signature Image containing signature
+ * @v name Required common name, or NULL to allow any name
+ * @ret rc Return status code
+ */
+int imgverify ( struct image *image, struct image *signature,
+ const char *name ) {
+ size_t len;
+ void *data;
+ struct cms_signature *sig;
+ struct cms_signer_info *info;
+ time_t now;
+ int rc;
+
+ /* Mark image as untrusted */
+ image_untrust ( image );
+
+ /* Copy signature to internal memory */
+ len = signature->len;
+ data = malloc ( len );
+ if ( ! data ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ copy_from_user ( data, signature->data, 0, len );
+
+ /* Parse signature */
+ if ( ( rc = cms_signature ( data, len, &sig ) ) != 0 )
+ goto err_parse;
+
+ /* Free internal copy of signature */
+ free ( data );
+ data = NULL;
+
+ /* Complete all certificate chains */
+ list_for_each_entry ( info, &sig->info, list ) {
+ if ( ( rc = create_validator ( &monojob, info->chain ) ) != 0 )
+ goto err_create_validator;
+ if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 )
+ goto err_validator_wait;
+ }
+
+ /* Use signature to verify image */
+ now = time ( NULL );
+ if ( ( rc = cms_verify ( sig, image->data, image->len,
+ name, now, NULL, NULL ) ) != 0 )
+ goto err_verify;
+
+ /* Drop reference to signature */
+ cms_put ( sig );
+ sig = NULL;
+
+ /* Mark image as trusted */
+ image_trust ( image );
+ syslog ( LOG_NOTICE, "Image \"%s\" signature OK\n", image->name );
+
+ return 0;
+
+ err_verify:
+ err_validator_wait:
+ err_create_validator:
+ cms_put ( sig );
+ err_parse:
+ free ( data );
+ err_alloc:
+ syslog ( LOG_ERR, "Image \"%s\" signature bad: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+}
diff --git a/qemu/roms/ipxe/src/usr/ipstat.c b/qemu/roms/ipxe/src/usr/ipstat.c
new file mode 100644
index 000000000..95ad799dc
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/ipstat.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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 <stdio.h>
+#include <ipxe/ipstat.h>
+#include <usr/ipstat.h>
+
+/** @file
+ *
+ * IP statistics
+ *
+ */
+
+/**
+ * Print IP statistics
+ *
+ */
+void ipstat ( void ) {
+ struct ip_statistics_family *family;
+ struct ip_statistics *stats;
+
+ for_each_table_entry ( family, IP_STATISTICS_FAMILIES ) {
+ stats = family->stats;
+ printf ( "IP version %d:\n", family->version );
+ printf ( " InReceives:%ld InMcastPkts:%ld InBcastPkts:%ld "
+ "InOctets:%ld\n", stats->in_receives,
+ stats->in_mcast_pkts, stats->in_bcast_pkts,
+ stats->in_octets );
+ printf ( " InHdrErrors:%ld InAddrErrors:%ld "
+ "InUnknownProtos:%ld InTruncatedPkts:%ld\n",
+ stats->in_hdr_errors, stats->in_addr_errors,
+ stats->in_unknown_protos, stats->in_truncated_pkts );
+ printf ( " ReasmReqds:%ld ReasmOKs:%ld ReasmFails:%ld\n",
+ stats->reasm_reqds, stats->reasm_oks,
+ stats->reasm_fails );
+ printf ( " InDelivers:%ld OutRequests:%ld OutNoRoutes:%ld\n",
+ stats->in_delivers, stats->out_requests,
+ stats->out_no_routes );
+ printf ( " OutTransmits:%ld OutMcastPkts:%ld OutBcastPkts:%ld "
+ "OutOctets:%ld\n", stats->out_transmits,
+ stats->out_mcast_pkts, stats->out_bcast_pkts,
+ stats->out_octets );
+ }
+}
diff --git a/qemu/roms/ipxe/src/usr/iwmgmt.c b/qemu/roms/ipxe/src/usr/iwmgmt.c
new file mode 100644
index 000000000..a486bceb3
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/iwmgmt.c
@@ -0,0 +1,226 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+#include <ipxe/net80211.h>
+#include <ipxe/ethernet.h>
+#include <usr/ifmgmt.h>
+#include <usr/iwmgmt.h>
+
+/** @file
+ *
+ * Wireless network interface management
+ *
+ */
+
+/**
+ * Print status of 802.11 device
+ *
+ * @v dev 802.11 device
+ */
+void iwstat ( struct net80211_device *dev ) {
+
+ ifstat ( dev->netdev );
+
+ printf ( " [802.11 ");
+ if ( dev->state & NET80211_ASSOCIATED ) {
+ printf ( "SSID '%s', ", dev->essid );
+ } else {
+ printf ( "not associated, " );
+ }
+ if ( dev->channel < dev->nr_channels && dev->rate < dev->nr_rates ) {
+ printf ( "Ch:%d Sig:%d", dev->channels[dev->channel].channel_nr,
+ dev->last_signal );
+ switch ( dev->hw->signal_type ) {
+ case NET80211_SIGNAL_NONE:
+ printf ( "?" );
+ break;
+ case NET80211_SIGNAL_ARBITRARY:
+ printf ( "/%d", dev->hw->signal_max );
+ break;
+ case NET80211_SIGNAL_DB:
+ printf ( "/%d dB", dev->hw->signal_max );
+ break;
+ case NET80211_SIGNAL_DBM:
+ printf ( " dBm" );
+ break;
+ }
+ printf ( ", Qual:%d%% Rate:%d Mbps]\n",
+ ( dev->rx_beacon_interval == 0 ? 0 :
+ 100 * dev->tx_beacon_interval /
+ dev->rx_beacon_interval ),
+ dev->rates[dev->rate] / 10 );
+ } else {
+ printf ( "antenna off]\n" );
+ }
+
+ if ( dev->state & NET80211_WORKING ) {
+ printf ( " [associating" );
+ if ( dev->associating )
+ printf ( " to '%s'", dev->associating->essid );
+ printf ( "...]\n" );
+ }
+}
+
+/** Identifiers for 802.11 cryptography types, indexed by type number */
+static const char *crypto_types[] = {
+ [NET80211_CRYPT_NONE] = "Open",
+ [NET80211_CRYPT_WEP] = "WEP ",
+ [NET80211_CRYPT_TKIP] = "WPA ",
+ [NET80211_CRYPT_CCMP] = "WPA2",
+ [NET80211_CRYPT_UNKNOWN] = "UNK ",
+};
+
+/** Number of 802.11 cryptography types defined */
+#define NR_CRYPTO_TYPES ( sizeof ( crypto_types ) / sizeof ( crypto_types[0] ) )
+
+/** Identifiers for 802.11 authentication types, indexed by type number */
+static const char *auth_types[] = {
+ [NET80211_SECPROT_NONE] = "",
+ [NET80211_SECPROT_PSK] = "PSK",
+ [NET80211_SECPROT_EAP] = "802.1X",
+ [NET80211_SECPROT_UNKNOWN] = "UNK",
+};
+
+/** Number of 802.11 authentication types defined */
+#define NR_AUTH_TYPES ( sizeof ( auth_types ) / sizeof ( auth_types[0] ) )
+
+/**
+ * Scan for wireless networks using 802.11 device
+ *
+ * @v dev 802.11 device
+ * @v active Whether to use active scanning
+ *
+ * The list of networks found will be printed in tabular format.
+ *
+ * This function is safe to call at all times, whether the 802.11
+ * device is open or not, but if called while the auto-association
+ * task is running it will return an error indication.
+ */
+int iwlist ( struct net80211_device *dev ) {
+ struct net80211_probe_ctx *ctx;
+ struct list_head *networks;
+ struct net80211_wlan *wlan;
+ char ssid_buf[22];
+ int rc;
+ unsigned i;
+ int was_opened = netdev_is_open ( dev->netdev );
+ int was_channel = dev->channels[dev->channel].channel_nr;
+
+ if ( ! was_opened ) {
+ dev->state |= NET80211_NO_ASSOC;
+ rc = netdev_open ( dev->netdev );
+ if ( rc < 0 )
+ goto err;
+ }
+
+ if ( dev->state & NET80211_WORKING ) {
+ rc = -EINVAL;
+ goto err_close_netdev;
+ }
+
+ if ( ! was_opened ) {
+ rc = net80211_prepare_probe ( dev, dev->hw->bands, 0 );
+ if ( rc < 0 )
+ goto err_close_netdev;
+ }
+
+ ctx = net80211_probe_start ( dev, "", 0 );
+ if ( ! ctx ) {
+ rc = -ENOMEM;
+ goto err_close_netdev;
+ }
+
+ while ( ! ( rc = net80211_probe_step ( ctx ) ) ) {
+ step();
+ }
+
+ networks = net80211_probe_finish_all ( ctx );
+
+ if ( list_empty ( networks ) ) {
+ goto err_free_networks;
+ }
+
+ rc = 0;
+
+ printf ( "Networks on %s:\n\n", dev->netdev->name );
+
+ /* Output format:
+ * 0 1 2 3 4 5 6
+ * 0123456789012345678901234567890123456789012345678901234567890
+ * [Sig] SSID BSSID Ch Crypt/Auth
+ * -------------------------------------------------------------
+ * [ 15] abcdefghijklmnopqrst> 00:00:00:00:00:00 11 Open
+ * ... or WPA PSK etc.
+ */
+
+ /* Quoting the dashes and spaces verbatim uses less code space
+ than generating them programmatically. */
+ printf ( "[Sig] SSID BSSID Ch Crypt/Auth\n"
+ "-------------------------------------------------------------\n" );
+
+ list_for_each_entry ( wlan, networks, list ) {
+
+ /* Format SSID into 22-character string, space-padded,
+ with '>' indicating truncation */
+
+ snprintf ( ssid_buf, sizeof ( ssid_buf ), "%s", wlan->essid );
+ for ( i = strlen ( ssid_buf ); i < sizeof ( ssid_buf ) - 1;
+ i++ )
+ ssid_buf[i] = ' ';
+ if ( ssid_buf[sizeof ( ssid_buf ) - 2] != ' ' )
+ ssid_buf[sizeof ( ssid_buf ) - 2] = '>';
+ ssid_buf[sizeof ( ssid_buf ) - 1] = 0;
+
+ /* Sanity check */
+ if ( wlan->crypto >= NR_CRYPTO_TYPES ||
+ wlan->handshaking >= NR_AUTH_TYPES )
+ continue;
+
+ printf ( "[%3d] %s %s %2d %s %s\n",
+ wlan->signal < 0 ? 100 + wlan->signal : wlan->signal,
+ ssid_buf, eth_ntoa ( wlan->bssid ), wlan->channel,
+ crypto_types[wlan->crypto],
+ auth_types[wlan->handshaking] );
+ }
+ printf ( "\n" );
+
+ err_free_networks:
+ net80211_free_wlanlist ( networks );
+
+ err_close_netdev:
+ if ( ! was_opened ) {
+ dev->state &= ~NET80211_NO_ASSOC;
+ netdev_close ( dev->netdev );
+ } else {
+ net80211_change_channel ( dev, was_channel );
+ }
+
+ if ( ! rc )
+ return 0;
+
+ err:
+ printf ( "Scanning for networks on %s: %s\n",
+ dev->netdev->name, strerror ( rc ) );
+ return rc;
+}
diff --git a/qemu/roms/ipxe/src/usr/lotest.c b/qemu/roms/ipxe/src/usr/lotest.c
new file mode 100644
index 000000000..ad7a2fad7
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/lotest.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2010 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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/keys.h>
+#include <ipxe/console.h>
+#include <usr/ifmgmt.h>
+#include <usr/lotest.h>
+
+/** @file
+ *
+ * Loopback testing
+ *
+ */
+
+/** Current loopback test receiver */
+static struct net_device *lotest_receiver;
+
+/** Loopback testing received packets */
+static LIST_HEAD ( lotest_queue );
+
+/**
+ * Process received packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_dest Link-layer destination address
+ * @v ll_source Link-layer source address
+ * @v flags Packet flags
+ * @ret rc Return status code
+ */
+static int lotest_rx ( struct io_buffer *iobuf,
+ struct net_device *netdev,
+ const void *ll_dest __unused,
+ const void *ll_source __unused,
+ unsigned int flags __unused ) {
+
+ /* Add to received packet queue if currently performing a test */
+ if ( netdev == lotest_receiver ) {
+ list_add_tail ( &iobuf->list, &lotest_queue );
+ } else {
+ free_iob ( iobuf );
+ }
+
+ return 0;
+}
+
+/**
+ * Dequeue received packet
+ *
+ * @ret iobuf I/O buffer, or NULL
+ */
+static struct io_buffer * lotest_dequeue ( void ) {
+ struct io_buffer *iobuf;
+
+ /* Remove first packet (if any) from received packet queue */
+ iobuf = list_first_entry ( &lotest_queue, struct io_buffer, list );
+ if ( ! iobuf )
+ return NULL;
+ list_del ( &iobuf->list );
+
+ return iobuf;
+}
+
+/**
+ * Transcribe network-layer address
+ *
+ * @v net_addr Network-layer address
+ * @ret string Human-readable transcription of address
+ */
+static const char * lotest_ntoa ( const void *net_addr __unused ) {
+ return "<INVALID>";
+}
+
+/**
+ * Loopback test network-layer protocol
+ *
+ * Using a dedicated network-layer protocol avoids problems caused by
+ * cards supporting features such as IPv4 checksum offload trying to
+ * interpret the (randomly generated) network-layer content.
+ */
+static struct net_protocol lotest_protocol __net_protocol = {
+ .name = "LOTEST",
+ .rx = lotest_rx,
+ .ntoa = lotest_ntoa,
+ .net_proto = htons ( 0x6950 ), /* Not a genuine protocol number */
+ .net_addr_len = 0,
+};
+
+/**
+ * Discard all received loopback test packets
+ *
+ */
+static void lotest_flush ( void ) {
+ struct io_buffer *iobuf;
+
+ while ( ( iobuf = lotest_dequeue() ) != NULL )
+ free_iob ( iobuf );
+}
+
+/**
+ * Wait for packet to be received
+ *
+ * @v data Expected data
+ * @v len Expected data length
+ * @ret rc Return status code
+ */
+static int loopback_wait ( void *data, size_t len ) {
+ struct io_buffer *iobuf;
+
+ /* Poll until packet arrives */
+ while ( 1 ) {
+
+ /* Check for cancellation */
+ if ( iskey() && ( getchar() == CTRL_C ) )
+ return -ECANCELED;
+
+ /* Poll network devices */
+ net_poll();
+
+ /* Dequeue packet, if available */
+ iobuf = lotest_dequeue();
+ if ( ! iobuf )
+ continue;
+
+ /* Check packet length */
+ if ( iob_len ( iobuf ) != len ) {
+ printf ( "\nLength mismatch: sent %zd, received %zd",
+ len, iob_len ( iobuf ) );
+ DBG ( "\nSent:\n" );
+ DBG_HDA ( 0, data, len );
+ DBG ( "Received:\n" );
+ DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iob_disown ( iobuf ) );
+ return -EINVAL;
+ }
+
+ /* Check packet content */
+ if ( memcmp ( iobuf->data, data, len ) != 0 ) {
+ printf ( "\nContent mismatch" );
+ DBG ( "\nSent:\n" );
+ DBG_HDA ( 0, data, len );
+ DBG ( "Received:\n" );
+ DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iob_disown ( iobuf ) );
+ return -EINVAL;
+ }
+
+ /* Discard packet and return */
+ free_iob ( iob_disown ( iobuf ) );
+ return 0;
+ }
+}
+
+/**
+ * Perform loopback test between two network devices
+ *
+ * @v sender Sending network device
+ * @v receiver Received network device
+ * @v mtu Packet size (excluding link-layer headers)
+ * @ret rc Return status code
+ */
+int loopback_test ( struct net_device *sender, struct net_device *receiver,
+ size_t mtu ) {
+ uint8_t *buf;
+ uint32_t *seq;
+ struct io_buffer *iobuf;
+ unsigned int i;
+ unsigned int successes;
+ int rc;
+
+ /* Open network devices */
+ if ( ( rc = ifopen ( sender ) ) != 0 )
+ return rc;
+ if ( ( rc = ifopen ( receiver ) ) != 0 )
+ return rc;
+
+ /* Wait for link-up */
+ if ( ( rc = iflinkwait ( sender, 0 ) ) != 0 )
+ return rc;
+ if ( ( rc = iflinkwait ( receiver, 0 ) ) != 0 )
+ return rc;
+
+ /* Allocate data buffer */
+ if ( mtu < sizeof ( *seq ) )
+ mtu = sizeof ( *seq );
+ buf = malloc ( mtu );
+ if ( ! buf )
+ return -ENOMEM;
+ seq = ( ( void * ) buf );
+
+ /* Print initial statistics */
+ printf ( "Performing loopback test from %s to %s with %zd byte MTU\n",
+ sender->name, receiver->name, mtu );
+ ifstat ( sender );
+ ifstat ( receiver );
+
+ /* Start loopback test */
+ lotest_flush();
+ lotest_receiver = receiver;
+
+ /* Perform loopback test */
+ for ( successes = 0 ; ; successes++ ) {
+
+ /* Print running total */
+ printf ( "\r%d", successes );
+
+ /* Generate random packet */
+ *seq = htonl ( successes );
+ for ( i = sizeof ( *seq ) ; i < mtu ; i++ )
+ buf[i] = random();
+ iobuf = alloc_iob ( MAX_LL_HEADER_LEN + mtu );
+ if ( ! iobuf ) {
+ printf ( "\nFailed to allocate I/O buffer" );
+ rc = -ENOMEM;
+ break;
+ }
+ iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
+ memcpy ( iob_put ( iobuf, mtu ), buf, mtu );
+
+ /* Transmit packet */
+ if ( ( rc = net_tx ( iob_disown ( iobuf ), sender,
+ &lotest_protocol, receiver->ll_addr,
+ sender->ll_addr ) ) != 0 ) {
+ printf ( "\nFailed to transmit packet: %s",
+ strerror ( rc ) );
+ break;
+ }
+
+ /* Wait for received packet */
+ if ( ( rc = loopback_wait ( buf, mtu ) ) != 0 )
+ break;
+ }
+
+ printf ( "\n");
+
+ /* Stop loopback testing */
+ lotest_receiver = NULL;
+ lotest_flush();
+
+ /* Dump final statistics */
+ ifstat ( sender );
+ ifstat ( receiver );
+
+ /* Free buffer */
+ free ( buf );
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/usr/neighmgmt.c b/qemu/roms/ipxe/src/usr/neighmgmt.c
new file mode 100644
index 000000000..e4d21a208
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/neighmgmt.c
@@ -0,0 +1,56 @@
+/*
+ * 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 <stdio.h>
+#include <ipxe/neighbour.h>
+#include <usr/neighmgmt.h>
+
+/** @file
+ *
+ * Neighbour management
+ *
+ */
+
+/**
+ * Print neighbour table
+ *
+ */
+void nstat ( void ) {
+ struct neighbour *neighbour;
+ struct net_device *netdev;
+ struct ll_protocol *ll_protocol;
+ struct net_protocol *net_protocol;
+
+ list_for_each_entry ( neighbour, &neighbours, list ) {
+ netdev = neighbour->netdev;
+ ll_protocol = netdev->ll_protocol;
+ net_protocol = neighbour->net_protocol;
+ printf ( "%s %s %s is %s %s", netdev->name, net_protocol->name,
+ net_protocol->ntoa ( neighbour->net_dest ),
+ ll_protocol->name,
+ ( neighbour_has_ll_dest ( neighbour ) ?
+ ll_protocol->ntoa ( neighbour->ll_dest ) :
+ "(incomplete)" ) );
+ if ( neighbour->discovery )
+ printf ( " (%s)", neighbour->discovery->name );
+ printf ( "\n" );
+ }
+}
diff --git a/qemu/roms/ipxe/src/usr/nslookup.c b/qemu/roms/ipxe/src/usr/nslookup.c
new file mode 100644
index 000000000..eb2b08b42
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/nslookup.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Patrick Plenefisch <phplenefisch@wpi.edu>.
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/resolv.h>
+#include <ipxe/tcpip.h>
+#include <ipxe/monojob.h>
+#include <ipxe/settings.h>
+#include <usr/nslookup.h>
+
+/** @file
+ *
+ * Standalone name resolution
+ *
+ */
+
+/** A name resolution request */
+struct nslookup {
+ /** Reference count for this object */
+ struct refcnt refcnt;
+
+ /** Job control interface */
+ struct interface job;
+ /** Data transfer interface */
+ struct interface resolver;
+
+ /** Setting name */
+ char *setting_name;
+};
+
+/**
+ * Terminate name resolution
+ *
+ * @v nslookup Name resolution request
+ * @v rc Reason for termination
+ */
+static void nslookup_close ( struct nslookup *nslookup, int rc ) {
+
+ /* Shut down interfaces */
+ intf_shutdown ( &nslookup->resolver, rc );
+ intf_shutdown ( &nslookup->job, rc );
+}
+
+/**
+ * Handle resolved name
+ *
+ * @v nslookup Name resolution request
+ * @v sa Completed socket address
+ */
+static void nslookup_resolv_done ( struct nslookup *nslookup,
+ struct sockaddr *sa ) {
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ const struct setting_type *default_type;
+ struct settings *settings;
+ struct setting setting;
+ void *data;
+ size_t len;
+ int rc;
+
+ /* Extract address */
+ switch ( sa->sa_family ) {
+ case AF_INET:
+ sin = ( ( struct sockaddr_in * ) sa );
+ data = &sin->sin_addr;
+ len = sizeof ( sin->sin_addr );
+ default_type = &setting_type_ipv4;
+ break;
+ case AF_INET6:
+ sin6 = ( ( struct sockaddr_in6 * ) sa );
+ data = &sin6->sin6_addr;
+ len = sizeof ( sin6->sin6_addr );
+ default_type = &setting_type_ipv6;
+ break;
+ default:
+ rc = -ENOTSUP;
+ goto err;
+ }
+
+ /* Parse specified setting name */
+ if ( ( rc = parse_setting_name ( nslookup->setting_name,
+ autovivify_child_settings, &settings,
+ &setting ) ) != 0 )
+ goto err;
+
+ /* Apply default type if necessary */
+ if ( ! setting.type )
+ setting.type = default_type;
+
+ /* Store in specified setting */
+ if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 )
+ goto err;
+
+ err:
+ /* Terminate name resolution */
+ nslookup_close ( nslookup, rc );
+}
+
+/** Name resolution resolver interface operations */
+static struct interface_operation nslookup_resolver_operations[] = {
+ INTF_OP ( resolv_done, struct nslookup *, nslookup_resolv_done ),
+ INTF_OP ( intf_close, struct nslookup *, nslookup_close ),
+};
+
+/** Name resolution resolver interface descriptor */
+static struct interface_descriptor nslookup_resolver_desc =
+ INTF_DESC_PASSTHRU ( struct nslookup, resolver,
+ nslookup_resolver_operations, job );
+
+/** Name resolution job control interface operations */
+static struct interface_operation nslookup_job_operations[] = {
+ INTF_OP ( intf_close, struct nslookup *, nslookup_close ),
+};
+
+/** Name resolution job control interface descriptor */
+static struct interface_descriptor nslookup_job_desc =
+ INTF_DESC_PASSTHRU ( struct nslookup, job,
+ nslookup_job_operations, resolver );
+
+/**
+ * Initiate standalone name resolution
+ *
+ * @v job Parent interface
+ * @v name Name to resolve
+ * @v setting_name Setting name
+ * @ret rc Return status code
+ */
+static int resolv_setting ( struct interface *job, const char *name,
+ const char *setting_name ) {
+ struct nslookup *nslookup;
+ struct sockaddr sa;
+ char *setting_name_copy;
+ int rc;
+
+ /* Allocate and initialise structure */
+ nslookup = zalloc ( sizeof ( *nslookup ) + strlen ( setting_name )
+ + 1 /* NUL */ );
+ if ( ! nslookup )
+ return -ENOMEM;
+ ref_init ( &nslookup->refcnt, NULL );
+ intf_init ( &nslookup->job, &nslookup_job_desc, &nslookup->refcnt );
+ intf_init ( &nslookup->resolver, &nslookup_resolver_desc,
+ &nslookup->refcnt );
+ setting_name_copy = ( ( void * ) ( nslookup + 1 ) );
+ strcpy ( setting_name_copy, setting_name );
+ nslookup->setting_name = setting_name_copy;
+
+ /* Start name resolution */
+ memset ( &sa, 0, sizeof ( sa ) );
+ if ( ( rc = resolv ( &nslookup->resolver, name, &sa ) ) != 0 )
+ goto err_resolv;
+
+ /* Attach parent interface, mortalise self, and return */
+ intf_plug_plug ( &nslookup->job, job );
+ ref_put ( &nslookup->refcnt );
+ return 0;
+
+ err_resolv:
+ ref_put ( &nslookup->refcnt );
+ return rc;
+}
+
+/**
+ * Perform (blocking) standalone name resolution
+ *
+ * @v name Name to resolve
+ * @v setting_name Setting name
+ * @ret rc Return status code
+ */
+int nslookup ( const char *name, const char *setting_name ) {
+ int rc;
+
+ /* Perform name resolution */
+ if ( ( rc = resolv_setting ( &monojob, name, setting_name ) ) == 0 )
+ rc = monojob_wait ( NULL, 0 );
+ if ( rc != 0 ) {
+ printf ( "Could not resolve %s: %s\n", name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/usr/pingmgmt.c b/qemu/roms/ipxe/src/usr/pingmgmt.c
new file mode 100644
index 000000000..16b3ec994
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/pingmgmt.c
@@ -0,0 +1,84 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <ipxe/pinger.h>
+#include <ipxe/monojob.h>
+#include <ipxe/timer.h>
+#include <usr/pingmgmt.h>
+
+/** @file
+ *
+ * ICMP ping management
+ *
+ */
+
+/**
+ * Display ping result
+ *
+ * @v src Source socket address, or NULL
+ * @v sequence Sequence number
+ * @v len Payload length
+ * @v rc Status code
+ */
+static void ping_callback ( struct sockaddr *peer, unsigned int sequence,
+ size_t len, int rc ) {
+
+ /* Display ping response */
+ printf ( "%zd bytes from %s: seq=%d",
+ len, ( peer ? sock_ntoa ( peer ) : "<none>" ), sequence );
+ if ( rc != 0 )
+ printf ( ": %s", strerror ( rc ) );
+ printf ( "\n" );
+}
+
+/**
+ * Ping a host
+ *
+ * @v hostname Hostname
+ * @v timeout Timeout between pings, in ticks
+ * @v len Payload length
+ * @v count Number of packets to send (or zero for no limit)
+ * @v quiet Inhibit output
+ * @ret rc Return status code
+ */
+int ping ( const char *hostname, unsigned long timeout, size_t len,
+ unsigned int count, int quiet ) {
+ int rc;
+
+ /* Create pinger */
+ if ( ( rc = create_pinger ( &monojob, hostname, timeout, len, count,
+ ( quiet ? NULL : ping_callback ) ) ) != 0 ){
+ printf ( "Could not start ping: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Wait for ping to complete */
+ if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) {
+ if ( ! quiet )
+ printf ( "Finished: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/usr/profstat.c b/qemu/roms/ipxe/src/usr/profstat.c
new file mode 100644
index 000000000..991427473
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/profstat.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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 <stdio.h>
+#include <ipxe/profile.h>
+#include <usr/profstat.h>
+
+/** @file
+ *
+ * Profiling
+ *
+ */
+
+/**
+ * Print profiling statistics
+ *
+ */
+void profstat ( void ) {
+ struct profiler *profiler;
+
+ for_each_table_entry ( profiler, PROFILERS ) {
+ printf ( "%s: %ld +/- %ld ticks (%d samples)\n",
+ profiler->name, profile_mean ( profiler ),
+ profile_stddev ( profiler ), profiler->count );
+ }
+}
diff --git a/qemu/roms/ipxe/src/usr/prompt.c b/qemu/roms/ipxe/src/usr/prompt.c
new file mode 100644
index 000000000..957b4ab3d
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/prompt.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 );
+
+/** @file
+ *
+ * Prompt for keypress
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ipxe/console.h>
+#include <usr/prompt.h>
+
+/**
+ * Prompt for keypress
+ *
+ * @v text Prompt string
+ * @v timeout Timeout period, in ticks (0=indefinite)
+ * @v key Key to wait for (0=any key)
+ * @ret rc Return status code
+ *
+ * Returns success if the specified key was pressed within the
+ * specified timeout period.
+ */
+int prompt ( const char *text, unsigned long timeout, int key ) {
+ int key_pressed;
+
+ /* Display prompt */
+ printf ( "%s", text );
+
+ /* Wait for key */
+ key_pressed = getkey ( timeout );
+
+ /* Clear the prompt line */
+ while ( *(text++) )
+ printf ( "\b \b" );
+
+ /* Check for timeout */
+ if ( key_pressed < 0 )
+ return -ETIMEDOUT;
+
+ /* Check for correct key pressed */
+ if ( key && ( key_pressed != key ) )
+ return -ECANCELED;
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/usr/pxemenu.c b/qemu/roms/ipxe/src/usr/pxemenu.c
new file mode 100644
index 000000000..b69905df1
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/pxemenu.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2009 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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <byteswap.h>
+#include <curses.h>
+#include <ipxe/console.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/keys.h>
+#include <ipxe/timer.h>
+#include <ipxe/uri.h>
+#include <ipxe/ansicol.h>
+#include <usr/dhcpmgmt.h>
+#include <usr/autoboot.h>
+
+/** @file
+ *
+ * PXE Boot Menus
+ *
+ */
+
+/** A PXE boot menu item */
+struct pxe_menu_item {
+ /** Boot Server type */
+ unsigned int type;
+ /** Description */
+ char *desc;
+};
+
+/**
+ * A PXE boot menu
+ *
+ * This structure encapsulates the menu information provided via DHCP
+ * options.
+ */
+struct pxe_menu {
+ /** Prompt string (optional) */
+ const char *prompt;
+ /** Timeout (in seconds)
+ *
+ * Negative indicates no timeout (i.e. wait indefinitely)
+ */
+ int timeout;
+ /** Number of menu items */
+ unsigned int num_items;
+ /** Selected menu item */
+ unsigned int selection;
+ /** Menu items */
+ struct pxe_menu_item items[0];
+};
+
+/**
+ * Parse and allocate PXE boot menu
+ *
+ * @v menu PXE boot menu to fill in
+ * @ret rc Return status code
+ *
+ * It is the callers responsibility to eventually free the allocated
+ * boot menu.
+ */
+static int pxe_menu_parse ( struct pxe_menu **menu ) {
+ struct setting pxe_boot_menu_prompt_setting =
+ { .tag = DHCP_PXE_BOOT_MENU_PROMPT };
+ struct setting pxe_boot_menu_setting =
+ { .tag = DHCP_PXE_BOOT_MENU };
+ uint8_t raw_menu[256];
+ int raw_prompt_len;
+ int raw_menu_len;
+ struct dhcp_pxe_boot_menu *raw_menu_item;
+ struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
+ void *raw_menu_end;
+ unsigned int num_menu_items;
+ unsigned int i;
+ int rc;
+
+ /* Fetch raw menu */
+ memset ( raw_menu, 0, sizeof ( raw_menu ) );
+ if ( ( raw_menu_len = fetch_raw_setting ( NULL, &pxe_boot_menu_setting,
+ raw_menu,
+ sizeof ( raw_menu ) ) ) < 0 ){
+ rc = raw_menu_len;
+ DBG ( "Could not retrieve raw PXE boot menu: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ if ( raw_menu_len >= ( int ) sizeof ( raw_menu ) ) {
+ DBG ( "Raw PXE boot menu too large for buffer\n" );
+ return -ENOSPC;
+ }
+ raw_menu_end = ( raw_menu + raw_menu_len );
+
+ /* Fetch raw prompt length */
+ raw_prompt_len =
+ fetch_raw_setting ( NULL, &pxe_boot_menu_prompt_setting,
+ NULL, 0 );
+ if ( raw_prompt_len < 0 )
+ raw_prompt_len = 0;
+
+ /* Count menu items */
+ num_menu_items = 0;
+ raw_menu_item = ( ( void * ) raw_menu );
+ while ( 1 ) {
+ if ( ( ( ( void * ) raw_menu_item ) +
+ sizeof ( *raw_menu_item ) ) > raw_menu_end )
+ break;
+ if ( ( ( ( void * ) raw_menu_item ) +
+ sizeof ( *raw_menu_item ) +
+ raw_menu_item->desc_len ) > raw_menu_end )
+ break;
+ num_menu_items++;
+ raw_menu_item = ( ( ( void * ) raw_menu_item ) +
+ sizeof ( *raw_menu_item ) +
+ raw_menu_item->desc_len );
+ }
+
+ /* Allocate space for parsed menu */
+ *menu = zalloc ( sizeof ( **menu ) +
+ ( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
+ raw_menu_len + 1 /* NUL */ +
+ raw_prompt_len + 1 /* NUL */ );
+ if ( ! *menu ) {
+ DBG ( "Could not allocate PXE boot menu\n" );
+ return -ENOMEM;
+ }
+
+ /* Fill in parsed menu */
+ (*menu)->num_items = num_menu_items;
+ raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
+ ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
+ memcpy ( raw_menu_item, raw_menu, raw_menu_len );
+ for ( i = 0 ; i < num_menu_items ; i++ ) {
+ (*menu)->items[i].type = le16_to_cpu ( raw_menu_item->type );
+ (*menu)->items[i].desc = raw_menu_item->desc;
+ /* Set type to 0; this ensures that the description
+ * for the previous menu item is NUL-terminated.
+ * (Final item is NUL-terminated anyway.)
+ */
+ raw_menu_item->type = 0;
+ raw_menu_item = ( ( ( void * ) raw_menu_item ) +
+ sizeof ( *raw_menu_item ) +
+ raw_menu_item->desc_len );
+ }
+ if ( raw_prompt_len ) {
+ raw_menu_prompt = ( ( ( void * ) raw_menu_item ) +
+ 1 /* NUL */ );
+ fetch_raw_setting ( NULL, &pxe_boot_menu_prompt_setting,
+ raw_menu_prompt, raw_prompt_len );
+ (*menu)->timeout =
+ ( ( raw_menu_prompt->timeout == 0xff ) ?
+ -1 : raw_menu_prompt->timeout );
+ (*menu)->prompt = raw_menu_prompt->prompt;
+ } else {
+ (*menu)->timeout = -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Draw PXE boot menu item
+ *
+ * @v menu PXE boot menu
+ * @v index Index of item to draw
+ * @v selected Item is selected
+ */
+static void pxe_menu_draw_item ( struct pxe_menu *menu,
+ unsigned int index, int selected ) {
+ char buf[COLS+1];
+ size_t len;
+ unsigned int row;
+
+ /* Prepare space-padded row content */
+ len = snprintf ( buf, sizeof ( buf ), " %c. %s",
+ ( 'A' + index ), menu->items[index].desc );
+ while ( len < ( sizeof ( buf ) - 1 ) )
+ buf[len++] = ' ';
+ buf[ sizeof ( buf ) - 1 ] = '\0';
+
+ /* Draw row */
+ row = ( LINES - menu->num_items + index );
+ color_set ( ( selected ? CPAIR_PXE : CPAIR_DEFAULT ), NULL );
+ mvprintw ( row, 0, "%s", buf );
+ move ( row, 1 );
+}
+
+/**
+ * Make selection from PXE boot menu
+ *
+ * @v menu PXE boot menu
+ * @ret rc Return status code
+ */
+static int pxe_menu_select ( struct pxe_menu *menu ) {
+ int key;
+ unsigned int key_selection;
+ unsigned int i;
+ int rc = 0;
+
+ /* Initialise UI */
+ initscr();
+ start_color();
+ color_set ( CPAIR_DEFAULT, NULL );
+
+ /* Draw initial menu */
+ for ( i = 0 ; i < menu->num_items ; i++ )
+ printf ( "\n" );
+ for ( i = 0 ; i < menu->num_items ; i++ )
+ pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 );
+
+ while ( 1 ) {
+
+ /* Highlight currently selected item */
+ pxe_menu_draw_item ( menu, menu->selection, 1 );
+
+ /* Wait for keyboard input */
+ key = getkey ( 0 );
+
+ /* Unhighlight currently selected item */
+ pxe_menu_draw_item ( menu, menu->selection, 0 );
+
+ /* Act upon key */
+ if ( ( key == CR ) || ( key == LF ) ) {
+ pxe_menu_draw_item ( menu, menu->selection, 1 );
+ break;
+ } else if ( ( key == CTRL_C ) || ( key == ESC ) ) {
+ rc = -ECANCELED;
+ break;
+ } else if ( key == KEY_UP ) {
+ if ( menu->selection > 0 )
+ menu->selection--;
+ } else if ( key == KEY_DOWN ) {
+ if ( menu->selection < ( menu->num_items - 1 ) )
+ menu->selection++;
+ } else if ( ( key < KEY_MIN ) &&
+ ( ( key_selection = ( toupper ( key ) - 'A' ) )
+ < menu->num_items ) ) {
+ menu->selection = key_selection;
+ pxe_menu_draw_item ( menu, menu->selection, 1 );
+ break;
+ }
+ }
+
+ /* Shut down UI */
+ endwin();
+
+ return rc;
+}
+
+/**
+ * Prompt for (and make selection from) PXE boot menu
+ *
+ * @v menu PXE boot menu
+ * @ret rc Return status code
+ */
+static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) {
+ unsigned long start = currticks();
+ unsigned long now;
+ unsigned long elapsed;
+ size_t len = 0;
+ int key;
+ int rc = 0;
+
+ /* Display menu immediately, if specified to do so */
+ if ( menu->timeout < 0 ) {
+ if ( menu->prompt )
+ printf ( "%s\n", menu->prompt );
+ return pxe_menu_select ( menu );
+ }
+
+ /* Display prompt, if specified */
+ if ( menu->prompt )
+ printf ( "%s", menu->prompt );
+
+ /* Wait for timeout, if specified */
+ while ( menu->timeout > 0 ) {
+ if ( ! len )
+ len = printf ( " (%d)", menu->timeout );
+ if ( iskey() ) {
+ key = getkey ( 0 );
+ if ( key == KEY_F8 ) {
+ /* Display menu */
+ printf ( "\n" );
+ return pxe_menu_select ( menu );
+ } else if ( ( key == CTRL_C ) || ( key == ESC ) ) {
+ /* Abort */
+ rc = -ECANCELED;
+ break;
+ } else {
+ /* Stop waiting */
+ break;
+ }
+ }
+ now = currticks();
+ elapsed = ( now - start );
+ if ( elapsed >= TICKS_PER_SEC ) {
+ menu->timeout -= 1;
+ do {
+ printf ( "\b \b" );
+ } while ( --len );
+ start = now;
+ }
+ }
+
+ /* Return with default option selected */
+ printf ( "\n" );
+ return rc;
+}
+
+/**
+ * Boot using PXE boot menu
+ *
+ * @ret rc Return status code
+ *
+ * Note that a success return status indicates that a PXE boot menu
+ * item has been selected, and that the DHCP session should perform a
+ * boot server request/ack.
+ */
+int pxe_menu_boot ( struct net_device *netdev ) {
+ struct pxe_menu *menu;
+ unsigned int pxe_type;
+ struct settings *pxebs_settings;
+ struct uri *uri;
+ int rc;
+
+ /* Parse and allocate boot menu */
+ if ( ( rc = pxe_menu_parse ( &menu ) ) != 0 )
+ return rc;
+
+ /* Make selection from boot menu */
+ if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) {
+ free ( menu );
+ return rc;
+ }
+ pxe_type = menu->items[menu->selection].type;
+
+ /* Free boot menu */
+ free ( menu );
+
+ /* Return immediately if local boot selected */
+ if ( ! pxe_type )
+ return 0;
+
+ /* Attempt PXE Boot Server Discovery */
+ if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 )
+ return rc;
+
+ /* Fetch next server and filename */
+ pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME );
+ assert ( pxebs_settings );
+ uri = fetch_next_server_and_filename ( pxebs_settings );
+ if ( ! uri )
+ return -ENOMEM;
+
+ /* Attempt boot */
+ rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN );
+ uri_put ( uri );
+ return rc;
+}
diff --git a/qemu/roms/ipxe/src/usr/route.c b/qemu/roms/ipxe/src/usr/route.c
new file mode 100644
index 000000000..ba4cc3221
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/route.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/netdevice.h>
+#include <usr/route.h>
+
+/** @file
+ *
+ * Routing management
+ *
+ */
+
+/**
+ * Print routing table
+ *
+ */
+void route ( void ) {
+ struct net_device *netdev;
+ struct routing_family *family;
+
+ for_each_netdev ( netdev ) {
+ for_each_table_entry ( family, ROUTING_FAMILIES ) {
+ family->print ( netdev );
+ }
+ }
+}
diff --git a/qemu/roms/ipxe/src/usr/route_ipv4.c b/qemu/roms/ipxe/src/usr/route_ipv4.c
new file mode 100644
index 000000000..b4d1b7bf3
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/route_ipv4.c
@@ -0,0 +1,58 @@
+/*
+ * 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 <stdio.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ip.h>
+#include <usr/route.h>
+
+/** @file
+ *
+ * IPv4 routing management
+ *
+ */
+
+/**
+ * Print IPv4 routing table
+ *
+ * @v netdev Network device
+ */
+static void route_ipv4_print ( struct net_device *netdev ) {
+ struct ipv4_miniroute *miniroute;
+
+ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+ if ( miniroute->netdev != netdev )
+ continue;
+ printf ( "%s: %s/", netdev->name,
+ inet_ntoa ( miniroute->address ) );
+ printf ( "%s", inet_ntoa ( miniroute->netmask ) );
+ if ( miniroute->gateway.s_addr )
+ printf ( " gw %s", inet_ntoa ( miniroute->gateway ) );
+ if ( ! netdev_is_open ( miniroute->netdev ) )
+ printf ( " (inaccessible)" );
+ printf ( "\n" );
+ }
+}
+
+/** IPv4 routing family */
+struct routing_family ipv4_routing_family __routing_family ( ROUTING_IPV4 ) = {
+ .print = route_ipv4_print,
+};
diff --git a/qemu/roms/ipxe/src/usr/route_ipv6.c b/qemu/roms/ipxe/src/usr/route_ipv6.c
new file mode 100644
index 000000000..6045f85bb
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/route_ipv6.c
@@ -0,0 +1,60 @@
+/*
+ * 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 <stdio.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ipv6.h>
+#include <usr/route.h>
+
+/** @file
+ *
+ * IPv6 routing management
+ *
+ */
+
+/**
+ * Print IPv6 routing table
+ *
+ * @v netdev Network device
+ */
+static void route_ipv6_print ( struct net_device *netdev ) {
+ struct ipv6_miniroute *miniroute;
+
+ list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
+ if ( miniroute->netdev != netdev )
+ continue;
+ printf ( "%s: %s/%d", netdev->name,
+ inet6_ntoa ( &miniroute->address ),
+ miniroute->prefix_len );
+ if ( miniroute->flags & IPV6_HAS_ROUTER )
+ printf ( " gw %s", inet6_ntoa ( &miniroute->router ) );
+ if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
+ printf ( " (no address)" );
+ if ( ! netdev_is_open ( miniroute->netdev ) )
+ printf ( " (inaccessible)" );
+ printf ( "\n" );
+ }
+}
+
+/** IPv6 routing family */
+struct routing_family ipv6_routing_family __routing_family ( ROUTING_IPV6 ) = {
+ .print = route_ipv6_print,
+};
diff --git a/qemu/roms/ipxe/src/usr/sync.c b/qemu/roms/ipxe/src/usr/sync.c
new file mode 100644
index 000000000..f7a04c44c
--- /dev/null
+++ b/qemu/roms/ipxe/src/usr/sync.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 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 <stddef.h>
+#include <ipxe/job.h>
+#include <ipxe/monojob.h>
+#include <ipxe/pending.h>
+#include <usr/sync.h>
+
+/** @file
+ *
+ * Wait for pending operations to complete
+ *
+ */
+
+/**
+ * Report progress
+ *
+ * @v intf Interface
+ * @v progress Progress report to fill in
+ * @ret ongoing_rc Ongoing job status code (if known)
+ */
+static int sync_progress ( struct interface *intf,
+ struct job_progress *progress __unused ) {
+
+ /* Terminate successfully if no pending operations remain */
+ if ( ! have_pending() )
+ intf_close ( intf, 0 );
+
+ return 0;
+}
+
+/** Synchroniser interface operations */
+static struct interface_operation sync_intf_op[] = {
+ INTF_OP ( job_progress, struct interface *, sync_progress ),
+};
+
+/** Synchroniser interface descriptor */
+static struct interface_descriptor sync_intf_desc =
+ INTF_DESC_PURE ( sync_intf_op );
+
+/** Synchroniser */
+static struct interface sync_intf = INTF_INIT ( sync_intf_desc );
+
+/**
+ * Wait for pending operations to complete
+ *
+ * @v timeout Timeout period, in ticks (0=indefinite)
+ * @ret rc Return status code
+ */
+int sync ( unsigned long timeout ) {
+
+ /* Attach synchroniser and wait for completion */
+ intf_plug_plug ( &monojob, &sync_intf );
+ return monojob_wait ( NULL, timeout );
+}