diff options
author | H. Peter Anvin <hpa@zytor.com> | 2009-02-17 20:17:17 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-02-17 20:17:17 -0800 |
commit | d0c6656a62113b913948361779d6298fe76f6e61 (patch) | |
tree | efa2541a1abae4760717c6db421ea818114ab6f7 /gpxe/src/usr | |
parent | 85b92a462dab7ce36c48614ea18314f8fc83ca9c (diff) | |
download | syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.gz syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.xz syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.zip |
Update gPXE to version 0.9.6+ 277b84c6e7d49f3cf01c855007f591de8c7cb75f
Update gPXE to version 0.9.6+, from commit
277b84c6e7d49f3cf01c855007f591de8c7cb75f in the main gPXE repository.
The only differences is src/config/general.h which has a few protocols
added, and src/arch/i386/prefix/boot1a.S which was called boot1a.s in
the upstream repository.
Diffstat (limited to 'gpxe/src/usr')
-rw-r--r-- | gpxe/src/usr/aoeboot.c | 73 | ||||
-rw-r--r-- | gpxe/src/usr/autoboot.c | 72 | ||||
-rw-r--r-- | gpxe/src/usr/dhcpmgmt.c | 11 | ||||
-rw-r--r-- | gpxe/src/usr/ifmgmt.c | 34 | ||||
-rw-r--r-- | gpxe/src/usr/imgmgmt.c | 13 | ||||
-rw-r--r-- | gpxe/src/usr/iscsiboot.c | 102 | ||||
-rw-r--r-- | gpxe/src/usr/pxemenu.c | 381 |
7 files changed, 471 insertions, 215 deletions
diff --git a/gpxe/src/usr/aoeboot.c b/gpxe/src/usr/aoeboot.c deleted file mode 100644 index f0e481bd..00000000 --- a/gpxe/src/usr/aoeboot.c +++ /dev/null @@ -1,73 +0,0 @@ -#include <stdint.h> -#include <string.h> -#include <stdio.h> -#include <byteswap.h> -#include <gpxe/aoe.h> -#include <gpxe/ata.h> -#include <gpxe/netdevice.h> -#include <gpxe/settings.h> -#include <gpxe/abft.h> -#include <int13.h> -#include <usr/aoeboot.h> - -/** - * Guess boot network device - * - * @ret netdev Boot network device - */ -static struct net_device * guess_boot_netdev ( void ) { - struct net_device *boot_netdev; - - /* Just use the first network device */ - for_each_netdev ( boot_netdev ) { - return boot_netdev; - } - - return NULL; -} - -int aoeboot ( const char *root_path ) { - struct ata_device ata; - struct int13_drive drive; - int rc; - - memset ( &ata, 0, sizeof ( ata ) ); - memset ( &drive, 0, sizeof ( drive ) ); - - printf ( "AoE booting from %s\n", root_path ); - - /* FIXME: ugly, ugly hack */ - struct net_device *netdev = guess_boot_netdev(); - - if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) { - printf ( "Could not attach AoE device: %s\n", - strerror ( rc ) ); - goto error_attach; - } - if ( ( rc = init_atadev ( &ata ) ) != 0 ) { - printf ( "Could not initialise AoE device: %s\n", - strerror ( rc ) ); - goto error_init; - } - - /* FIXME: ugly, ugly hack */ - struct aoe_session *aoe = - container_of ( ata.backend, struct aoe_session, refcnt ); - abft_fill_data ( aoe ); - - drive.blockdev = &ata.blockdev; - - register_int13_drive ( &drive ); - printf ( "Registered as BIOS drive %#02x\n", drive.drive ); - printf ( "Booting from BIOS drive %#02x\n", drive.drive ); - rc = int13_boot ( drive.drive ); - printf ( "Boot failed\n" ); - - printf ( "Unregistering BIOS drive %#02x\n", drive.drive ); - unregister_int13_drive ( &drive ); - - error_init: - aoe_detach ( &ata ); - error_attach: - return rc; -} diff --git a/gpxe/src/usr/autoboot.c b/gpxe/src/usr/autoboot.c index 326292b4..98e79a7f 100644 --- a/gpxe/src/usr/autoboot.c +++ b/gpxe/src/usr/autoboot.c @@ -23,14 +23,12 @@ #include <gpxe/dhcp.h> #include <gpxe/settings.h> #include <gpxe/image.h> -#include <gpxe/embedded.h> +#include <gpxe/sanboot.h> #include <gpxe/uri.h> #include <usr/ifmgmt.h> #include <usr/route.h> #include <usr/dhcpmgmt.h> #include <usr/imgmgmt.h> -#include <usr/iscsiboot.h> -#include <usr/aoeboot.h> #include <usr/autoboot.h> /** @file @@ -45,6 +43,12 @@ /** Shutdown flags for exit */ int shutdown_exit_flags = 0; +/* SAN boot protocols */ +static struct sanboot_protocol sanboot_protocols[0] \ + __table_start ( struct sanboot_protocol, sanboot_protocols ); +static struct sanboot_protocol sanboot_protocols_end[0] \ + __table_end ( struct sanboot_protocol, sanboot_protocols ); + /** * Identify the boot network device * @@ -55,37 +59,13 @@ static struct net_device * find_boot_netdev ( void ) { } /** - * Boot embedded image - * - * @ret rc Return status code - */ -static int boot_embedded_image ( void ) { - struct image *image; - int rc; - - image = embedded_image(); - if ( !image ) - return ENOENT; - - if ( ( rc = imgload ( image ) ) != 0 ) { - printf ( "Could not load embedded image: %s\n", - strerror ( rc ) ); - } else if ( ( rc = imgexec ( image ) ) != 0 ) { - printf ( "Could not boot embedded image: %s\n", - strerror ( rc ) ); - } - image_put ( image ); - return rc; -} - -/** * Boot using next-server and filename * * @v filename Boot filename * @ret rc Return status code */ -static int boot_next_server_and_filename ( struct in_addr next_server, - const char *filename ) { +int boot_next_server_and_filename ( struct in_addr next_server, + const char *filename ) { struct uri *uri; struct image *image; char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + strlen(filename) + 1 ]; @@ -141,12 +121,15 @@ static int boot_next_server_and_filename ( struct in_addr next_server, * @ret rc Return status code */ int boot_root_path ( const char *root_path ) { + struct sanboot_protocol *sanboot; /* Quick hack */ - if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) { - return iscsiboot ( root_path ); - } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) { - return aoeboot ( root_path ); + for ( sanboot = sanboot_protocols ; + sanboot < sanboot_protocols_end ; sanboot++ ) { + if ( strncmp ( root_path, sanboot->prefix, + strlen ( sanboot->prefix ) ) == 0 ) { + return sanboot->boot ( root_path ); + } } return -ENOTSUP; @@ -159,8 +142,15 @@ int boot_root_path ( const char *root_path ) { * @ret rc Return status code */ static int netboot ( struct net_device *netdev ) { + 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[256]; struct in_addr next_server; + unsigned int pxe_discovery_control; int rc; /* Open device and display device status */ @@ -181,10 +171,18 @@ static int netboot ( struct net_device *netdev ) { return rc; route(); - /* Try to boot an embedded image if we have one */ - rc = boot_embedded_image (); - if ( rc != ENOENT ) - return rc; + /* Try PXE menu boot, if applicable */ + fetch_string_setting ( NULL, &vendor_class_id_setting, + buf, sizeof ( buf ) ); + pxe_discovery_control = + fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + if ( ( strcmp ( buf, "PXEClient" ) == 0 ) && + setting_exists ( NULL, &pxe_boot_menu_setting ) && + ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && + setting_exists ( NULL, &filename_setting ) ) ) ) { + printf ( "Booting from PXE menu\n" ); + return pxe_menu_boot ( netdev ); + } /* Try to download and boot whatever we are given as a filename */ fetch_ipv4_setting ( NULL, &next_server_setting, &next_server ); diff --git a/gpxe/src/usr/dhcpmgmt.c b/gpxe/src/usr/dhcpmgmt.c index 2e429cd6..6acf7f6d 100644 --- a/gpxe/src/usr/dhcpmgmt.c +++ b/gpxe/src/usr/dhcpmgmt.c @@ -46,3 +46,14 @@ int dhcp ( struct net_device *netdev ) { return rc; } + +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 ( "" ); + + return rc; +} diff --git a/gpxe/src/usr/ifmgmt.c b/gpxe/src/usr/ifmgmt.c index 9c88ab53..0ebf5615 100644 --- a/gpxe/src/usr/ifmgmt.c +++ b/gpxe/src/usr/ifmgmt.c @@ -20,9 +20,11 @@ #include <stdio.h> #include <unistd.h> #include <errno.h> +#include <console.h> #include <gpxe/netdevice.h> #include <gpxe/device.h> #include <gpxe/process.h> +#include <gpxe/keys.h> #include <usr/ifmgmt.h> /** @file @@ -59,6 +61,25 @@ void ifclose ( struct net_device *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 @@ -69,8 +90,10 @@ void ifstat ( struct net_device *netdev ) { netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name, ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ), ( netdev_link_ok ( netdev ) ? "up" : "down" ), - netdev->stats.tx_ok, netdev->stats.tx_err, - netdev->stats.rx_ok, netdev->stats.rx_err ); + netdev->tx_stats.good, netdev->tx_stats.bad, + netdev->rx_stats.good, netdev->rx_stats.bad ); + ifstat_errors ( &netdev->tx_stats, "TXE" ); + ifstat_errors ( &netdev->rx_stats, "RXE" ); } /** @@ -80,12 +103,19 @@ void ifstat ( struct net_device *netdev ) { * @v max_wait_ms Maximum time to wait, in ms */ int iflinkwait ( struct net_device *netdev, unsigned int max_wait_ms ) { + int key; + while ( 1 ) { if ( netdev_link_ok ( netdev ) ) return 0; if ( max_wait_ms-- == 0 ) return -ETIMEDOUT; step(); + if ( iskey() ) { + key = getchar(); + if ( key == CTRL_C ) + return -ECANCELED; + } mdelay ( 1 ); } } diff --git a/gpxe/src/usr/imgmgmt.c b/gpxe/src/usr/imgmgmt.c index be153f87..bd53d824 100644 --- a/gpxe/src/usr/imgmgmt.c +++ b/gpxe/src/usr/imgmgmt.c @@ -43,7 +43,10 @@ */ int imgfetch ( struct image *image, const char *uri_string, int ( * image_register ) ( struct image *image ) ) { + char uri_string_redacted[ strlen ( uri_string ) + 3 /* "***" */ + + 1 /* NUL */ ]; struct uri *uri; + const char *password; int rc; if ( ! ( uri = parse_uri ( uri_string ) ) ) @@ -51,9 +54,17 @@ int imgfetch ( struct image *image, const char *uri_string, image_set_uri ( image, uri ); + /* Redact password portion of URI, if necessary */ + password = uri->password; + if ( password ) + uri->password = "***"; + unparse_uri ( uri_string_redacted, sizeof ( uri_string_redacted ), + uri ); + uri->password = password; + if ( ( rc = create_downloader ( &monojob, image, image_register, LOCATION_URI, uri ) ) == 0 ) - rc = monojob_wait ( uri_string ); + rc = monojob_wait ( uri_string_redacted ); uri_put ( uri ); return rc; diff --git a/gpxe/src/usr/iscsiboot.c b/gpxe/src/usr/iscsiboot.c deleted file mode 100644 index 84d77c45..00000000 --- a/gpxe/src/usr/iscsiboot.c +++ /dev/null @@ -1,102 +0,0 @@ -#include <stdint.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <gpxe/iscsi.h> -#include <gpxe/settings.h> -#include <gpxe/dhcp.h> -#include <gpxe/netdevice.h> -#include <gpxe/ibft.h> -#include <gpxe/init.h> -#include <int13.h> -#include <usr/autoboot.h> -#include <usr/iscsiboot.h> - -struct setting keep_san_setting __setting = { - .name = "keep-san", - .description = "Preserve SAN connection", - .tag = DHCP_EB_KEEP_SAN, - .type = &setting_type_int8, -}; - -/** - * Guess boot network device - * - * @ret netdev Boot network device - */ -static struct net_device * guess_boot_netdev ( void ) { - struct net_device *boot_netdev; - - /* Just use the first network device */ - for_each_netdev ( boot_netdev ) { - return boot_netdev; - } - - return NULL; -} - -int iscsiboot ( const char *root_path ) { - struct scsi_device *scsi; - struct int13_drive *drive; - int keep_san; - int rc; - - scsi = zalloc ( sizeof ( *scsi ) ); - if ( ! scsi ) { - rc = -ENOMEM; - goto err_alloc_scsi; - } - drive = zalloc ( sizeof ( *drive ) ); - if ( ! drive ) { - rc = -ENOMEM; - goto err_alloc_drive; - } - - printf ( "iSCSI booting from %s\n", root_path ); - - if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) { - printf ( "Could not attach iSCSI device: %s\n", - strerror ( rc ) ); - goto err_attach; - } - if ( ( rc = init_scsidev ( scsi ) ) != 0 ) { - printf ( "Could not initialise iSCSI device: %s\n", - strerror ( rc ) ); - goto err_init; - } - - drive->blockdev = &scsi->blockdev; - - /* FIXME: ugly, ugly hack */ - struct net_device *netdev = guess_boot_netdev(); - struct iscsi_session *iscsi = - container_of ( scsi->backend, struct iscsi_session, refcnt ); - ibft_fill_data ( netdev, iscsi ); - - register_int13_drive ( drive ); - printf ( "Registered as BIOS drive %#02x\n", drive->drive ); - printf ( "Booting from BIOS drive %#02x\n", drive->drive ); - rc = int13_boot ( drive->drive ); - printf ( "Boot failed\n" ); - - /* Leave drive registered, if instructed to do so */ - keep_san = fetch_intz_setting ( NULL, &keep_san_setting ); - if ( keep_san ) { - printf ( "Preserving connection to SAN disk\n" ); - shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES; - return rc; - } - - printf ( "Unregistering BIOS drive %#02x\n", drive->drive ); - unregister_int13_drive ( drive ); - - err_init: - iscsi_detach ( scsi ); - err_attach: - free ( drive ); - err_alloc_drive: - free ( scsi ); - err_alloc_scsi: - return rc; -} diff --git a/gpxe/src/usr/pxemenu.c b/gpxe/src/usr/pxemenu.c new file mode 100644 index 00000000..793cb624 --- /dev/null +++ b/gpxe/src/usr/pxemenu.c @@ -0,0 +1,381 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#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 <console.h> +#include <gpxe/dhcp.h> +#include <gpxe/keys.h> +#include <gpxe/timer.h> +#include <gpxe/process.h> +#include <usr/dhcpmgmt.h> +#include <usr/autoboot.h> + +/** @file + * + * PXE Boot Menus + * + */ + +/* Colour pairs */ +#define CPAIR_NORMAL 1 +#define CPAIR_SELECT 2 + +/** 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_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_setting_len ( NULL, + &pxe_boot_menu_prompt_setting ); + 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 = ntohs ( 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_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_SELECT : CPAIR_NORMAL ), 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(); + init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); + init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE ); + color_set ( CPAIR_NORMAL, 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 */ + while ( ! iskey() ) + step(); + key = getkey(); + + /* 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(); + 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 in_addr next_server; + char filename[256]; + 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; + + /* Attempt boot */ + pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); + assert ( pxebs_settings ); + fetch_ipv4_setting ( pxebs_settings, &next_server_setting, + &next_server ); + fetch_string_setting ( pxebs_settings, &filename_setting, + filename, sizeof ( filename ) ); + return boot_next_server_and_filename ( next_server, filename ); +} |