From 5d2802e4030ed9177c01e751fd89c898eaf90f88 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 23 Apr 2011 09:50:38 +0100 Subject: [PATCH] [sanboot] Add "sanhook" and "sanunhook" commands Expose the multiple-SAN-drive capability of the iPXE core via the iPXE command line by adding commands to hook and unhook additional drives. Signed-off-by: Michael Brown --- src/arch/i386/include/int13.h | 3 - src/arch/i386/include/ipxe/bios_sanboot.h | 11 ++ src/arch/i386/interface/pcbios/int13.c | 12 +- src/core/null_sanboot.c | 1 + src/hci/commands/sanboot_cmd.c | 164 +++++++++++++++++----- src/include/ipxe/null_sanboot.h | 5 + src/include/ipxe/sanboot.h | 11 +- src/include/usr/autoboot.h | 14 +- src/usr/autoboot.c | 99 +++++++------ src/usr/pxemenu.c | 2 +- 10 files changed, 230 insertions(+), 92 deletions(-) diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h index a14ebb2e..315cd1b8 100644 --- a/src/arch/i386/include/int13.h +++ b/src/arch/i386/include/int13.h @@ -261,7 +261,4 @@ struct master_boot_record { uint16_t magic; } __attribute__ (( packed )); -/** Use natural BIOS drive number */ -#define INT13_USE_NATURAL_DRIVE 0xff - #endif /* INT13_H */ diff --git a/src/arch/i386/include/ipxe/bios_sanboot.h b/src/arch/i386/include/ipxe/bios_sanboot.h index 09794548..689227b7 100644 --- a/src/arch/i386/include/ipxe/bios_sanboot.h +++ b/src/arch/i386/include/ipxe/bios_sanboot.h @@ -15,4 +15,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PREFIX_pcbios __pcbios_ #endif +/** + * Get default SAN drive number + * + * @ret drive Default drive number + */ +static inline __always_inline unsigned int +SANBOOT_INLINE ( pcbios, san_default_drive ) ( void ) { + /* Default to booting from first hard disk */ + return 0x80; +} + #endif /* _IPXE_BIOS_SANBOOT_H */ diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 392dbd5f..b764139e 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -1164,8 +1164,8 @@ static void int13_free ( struct refcnt *refcnt ) { * Hook INT 13 emulated drive * * @v uri URI - * @v drive Requested drive number - * @ret drive Assigned drive number, or negative error + * @v drive Drive number + * @ret rc Return status code * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). @@ -1176,12 +1176,9 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { unsigned int natural_drive; int rc; - /* Calculate drive number */ + /* Calculate natural drive number */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); natural_drive = ( num_drives | 0x80 ); - if ( drive == INT13_USE_NATURAL_DRIVE ) - drive = natural_drive; - drive |= 0x80; /* Check that drive number is not in use */ list_for_each_entry ( int13, &int13s, list ) { @@ -1231,7 +1228,7 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { /* Update BIOS drive count */ int13_set_num_drives(); - return int13->drive; + return 0; err_guess_geometry: err_reopen_block: @@ -1428,6 +1425,7 @@ static int int13_describe ( unsigned int drive ) { return 0; } +PROVIDE_SANBOOT_INLINE ( pcbios, san_default_drive ); PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook ); PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook ); PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot ); diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c index 9cdb1629..6584e7a1 100644 --- a/src/core/null_sanboot.c +++ b/src/core/null_sanboot.c @@ -38,6 +38,7 @@ static int null_san_describe ( unsigned int drive __unused ) { return -EOPNOTSUPP; } +PROVIDE_SANBOOT_INLINE ( null, san_default_drive ); PROVIDE_SANBOOT ( null, san_hook, null_san_hook ); PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook ); PROVIDE_SANBOOT ( null, san_boot, null_san_boot ); diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 198a7322..b4937104 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include FILE_LICENCE ( GPL2_OR_LATER ); @@ -34,15 +35,112 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** "sanboot" options */ -struct sanboot_options {}; +struct sanboot_options { + /** Drive number */ + unsigned int drive; + /** Do not describe SAN device */ + int no_describe; + /** Keep SAN device */ + int keep; +}; /** "sanboot" option list */ -static struct option_descriptor sanboot_opts[] = {}; +static struct option_descriptor sanboot_opts[] = { + OPTION_DESC ( "drive", 'd', required_argument, + struct sanboot_options, drive, parse_integer ), + OPTION_DESC ( "no-describe", 'n', no_argument, + struct sanboot_options, no_describe, parse_flag ), + OPTION_DESC ( "keep", 'k', no_argument, + struct sanboot_options, keep, parse_flag ), +}; + +/** "sanhook" command descriptor */ +static struct command_descriptor sanhook_cmd = + COMMAND_DESC ( struct sanboot_options, sanboot_opts, 1, 1, + "[--drive ] [--no-describe] " ); /** "sanboot" command descriptor */ static struct command_descriptor sanboot_cmd = - COMMAND_DESC ( struct sanboot_options, sanboot_opts, 1, 1, - "" ); + COMMAND_DESC ( struct sanboot_options, sanboot_opts, 0, 1, + "[--drive ] [--no-describe] [--keep] " + "[]" ); + +/** "sanunhook" command descriptor */ +static struct command_descriptor sanunhook_cmd = + COMMAND_DESC ( struct sanboot_options, sanboot_opts, 0, 0, + "[--drive ]" ); + +/** + * The "sanboot", "sanhook" and "sanunhook" commands + * + * @v argc Argument count + * @v argv Argument list + * @v default_flags Default set of flags for uriboot() + * @v no_root_path_flags Additional flags to apply if no root path is present + * @ret rc Return status code + */ +static int sanboot_core_exec ( int argc, char **argv, + struct command_descriptor *cmd, + int default_flags, int no_root_path_flags ) { + struct sanboot_options opts; + const char *root_path; + struct uri *uri; + int flags; + int rc; + + /* Initialise options */ + memset ( &opts, 0, sizeof ( opts ) ); + opts.drive = san_default_drive(); + + /* Parse options */ + if ( ( rc = reparse_options ( argc, argv, cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse root path, if present */ + if ( argc > optind ) { + root_path = argv[optind]; + uri = parse_uri ( root_path ); + if ( ! uri ) { + rc = -ENOMEM; + goto err_parse_uri; + } + } else { + root_path = NULL; + uri = NULL; + } + + /* Construct flags */ + flags = default_flags; + if ( opts.no_describe ) + flags |= URIBOOT_NO_SAN_DESCRIBE; + if ( opts.keep ) + flags |= URIBOOT_NO_SAN_UNHOOK; + if ( ! root_path ) + flags |= no_root_path_flags; + + /* Boot from root path */ + if ( ( rc = uriboot ( NULL, uri, opts.drive, flags ) ) != 0 ) + goto err_uriboot; + + err_uriboot: + uri_put ( uri ); + err_parse_uri: + err_parse_options: + return rc; +} + +/** + * The "sanhook" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int sanhook_exec ( int argc, char **argv ) { + return sanboot_core_exec ( argc, argv, &sanhook_cmd, + ( URIBOOT_NO_SAN_BOOT | + URIBOOT_NO_SAN_UNHOOK ), 0 ); +} /** * The "sanboot" command @@ -52,39 +150,35 @@ static struct command_descriptor sanboot_cmd = * @ret rc Return status code */ static int sanboot_exec ( int argc, char **argv ) { - struct sanboot_options opts; - const char *root_path; - struct uri *uri; - int rc; + return sanboot_core_exec ( argc, argv, &sanboot_cmd, + 0, URIBOOT_NO_SAN_UNHOOK ); +} - /* Parse options */ - if ( ( rc = parse_options ( argc, argv, &sanboot_cmd, &opts ) ) != 0 ) - goto err_parse_options; - - /* Parse root path */ - root_path = argv[optind]; - uri = parse_uri ( root_path ); - if ( ! uri ) { - rc = -ENOMEM; - goto err_parse_uri; - } - - /* Boot from root path */ - if ( ( rc = uriboot ( NULL, uri ) ) != 0 ) { - printf ( "Could not boot from %s: %s\n", - root_path, strerror ( rc ) ); - goto err_uriboot; - } - - err_uriboot: - uri_put ( uri ); - err_parse_uri: - err_parse_options: - return rc; +/** + * The "sanunhook" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int sanunhook_exec ( int argc, char **argv ) { + return sanboot_core_exec ( argc, argv, &sanunhook_cmd, + ( URIBOOT_NO_SAN_DESCRIBE | + URIBOOT_NO_SAN_BOOT ), 0 ); } /** SAN commands */ -struct command sanboot_command __command = { - .name = "sanboot", - .exec = sanboot_exec, +struct command sanboot_commands[] __command = { + { + .name = "sanhook", + .exec = sanhook_exec, + }, + { + .name = "sanboot", + .exec = sanboot_exec, + }, + { + .name = "sanunhook", + .exec = sanunhook_exec, + }, }; diff --git a/src/include/ipxe/null_sanboot.h b/src/include/ipxe/null_sanboot.h index 341a9a1d..2b3a2c74 100644 --- a/src/include/ipxe/null_sanboot.h +++ b/src/include/ipxe/null_sanboot.h @@ -15,4 +15,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PREFIX_null __null_ #endif +static inline __always_inline unsigned int +SANBOOT_INLINE ( null, san_default_drive ) ( void ) { + return 0; +} + #endif /* _IPXE_NULL_SANBOOT_H */ diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index 913282eb..14c8a5da 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -58,12 +58,19 @@ struct uri; /* Include all architecture-dependent sanboot API headers */ #include +/** + * Get default SAN drive number + * + * @ret drive Default drive number + */ +unsigned int san_default_drive ( void ); + /** * Hook SAN device * * @v uri URI - * @v drive Requested drive number - * @ret drive Assigned drive number, or negative error + * @v drive Drive number + * @ret rc Return status code */ int san_hook ( struct uri *uri, unsigned int drive ); diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index a608b3dc..25b9f073 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -14,7 +14,19 @@ struct net_device; struct uri; struct settings; -extern int uriboot ( struct uri *filename, struct uri *root_path ); +/** uriboot() flags */ +enum uriboot_flags { + URIBOOT_NO_SAN_DESCRIBE = 0x0001, + URIBOOT_NO_SAN_BOOT = 0x0002, + URIBOOT_NO_SAN_UNHOOK = 0x0004, +}; + +#define URIBOOT_NO_SAN ( URIBOOT_NO_SAN_DESCRIBE | \ + URIBOOT_NO_SAN_BOOT | \ + URIBOOT_NO_SAN_UNHOOK ) + +extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, + unsigned int flags ); extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index c5c5f3e7..63ca4c3d 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -119,54 +119,37 @@ struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA ) = { * * @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; +int uriboot ( struct uri *filename, struct uri *root_path, int drive, + unsigned int flags ) { int rc; - /* Treat empty URIs as absent */ - if ( filename && ( ! uri_has_path ( filename ) ) ) - filename = NULL; - if ( root_path && ( ! uri_is_absolute ( 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" ); - 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; - } - /* Hook SAN device, if applicable */ if ( root_path ) { - drive = san_hook ( root_path, 0 ); - if ( drive < 0 ) { - rc = drive; + if ( ( rc = san_hook ( root_path, drive ) ) != 0 ) { printf ( "Could not open SAN device: %s\n", strerror ( rc ) ); goto err_san_hook; } - printf ( "Registered as SAN device %#02x\n", drive ); - } else { - drive = -ENODEV; + printf ( "Registered SAN device %#02x\n", drive ); } /* Describe SAN device, if applicable */ - if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) { - printf ( "Could not describe SAN device %#02x: %s\n", - drive, strerror ( rc ) ); - goto err_san_describe; + 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 */ @@ -192,7 +175,7 @@ int uriboot ( struct uri *filename, struct uri *root_path ) { } /* Attempt SAN boot if applicable */ - if ( root_path ) { + 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 ); @@ -209,17 +192,15 @@ int uriboot ( struct uri *filename, struct uri *root_path ) { err_san_describe: /* Unhook SAN device, if applicable */ - if ( drive >= 0 ) { + if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { - printf ( "Unregistering SAN device %#02x\n", drive ); san_unhook ( drive ); + printf ( "Unregistered SAN device %#02x\n", drive ); } else { - printf ( "Preserving connection to SAN device %#02x\n", - drive ); + printf ( "Preserving SAN device %#02x\n", drive ); } } err_san_hook: - err_no_boot: return rc; } @@ -360,19 +341,51 @@ int netboot ( struct net_device *netdev ) { goto err_pxe_menu_boot; } - /* Fetch next server, filename and root path */ + /* 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 ) ) != 0 ) + 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 ); diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index 75789e2d..bc176563 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -377,7 +377,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { return -ENOMEM; /* Attempt boot */ - rc = uriboot ( uri, NULL ); + rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN ); uri_put ( uri ); return rc; }