[iSCSI] Support Windows Server 2008 direct iSCSI installation master
authorMichael Brown <mcb30@etherboot.org>
Thu, 17 Jul 2008 16:45:17 +0000 (17:45 +0100)
committerMichael Brown <mcb30@etherboot.org>
Thu, 17 Jul 2008 16:45:17 +0000 (17:45 +0100)
Add yet another ugly hack to iscsiboot.c, this time to allow the user to
inhibit the shutdown/removal of the iSCSI INT13 device (and the network
devices, since they are required for the iSCSI device to function).

On the plus side, the fact that shutdown() now takes flags to
differentiate between shutdown-for-exit and shutdown-for-boot means that
another ugly hack (to allow returning via the PXE stack on BIOSes that
have broken INT 18 calls) will be easier.

I feel dirty.

18 files changed:
src/arch/i386/firmware/pcbios/hidemem.c
src/arch/i386/image/bzimage.c
src/arch/i386/image/elfboot.c
src/arch/i386/image/multiboot.c
src/arch/i386/image/nbi.c
src/core/config.c
src/core/device.c
src/core/init.c
src/core/main.c
src/core/serial.c
src/include/gpxe/device.h
src/include/gpxe/dhcp.h
src/include/gpxe/errfile.h
src/include/gpxe/init.h
src/include/usr/autoboot.h
src/interface/pxe/pxe_preboot.c
src/usr/autoboot.c
src/usr/iscsiboot.c

index eba9400..11ca312 100644 (file)
@@ -128,7 +128,7 @@ static void hide_etherboot ( void ) {
  * Uninstalls the INT 15 handler installed by hide_etherboot(), if
  * possible.
  */
-static void unhide_etherboot ( void ) {
+static void unhide_etherboot ( int flags __unused ) {
 
        /* If we have more than one hooked interrupt at this point, it
         * means that some other vector is still hooked, in which case
index d9c0545..e6fd854 100644 (file)
@@ -348,7 +348,7 @@ static int bzimage_exec ( struct image *image ) {
                       sizeof ( bzhdr ) );
 
        /* Prepare for exiting */
-       shutdown();
+       shutdown ( SHUTDOWN_BOOT );
 
        DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
               "(stack %04x:%04zx)\n", image,
index 52510aa..60c5499 100644 (file)
@@ -46,7 +46,7 @@ static int elfboot_exec ( struct image *image ) {
        /* An ELF image has no callback interface, so we need to shut
         * down before invoking it.
         */
-       shutdown();
+       shutdown ( SHUTDOWN_BOOT );
 
        /* Jump to OS with flat physical addressing */
        __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" )
index fbaebd5..d7c2b8d 100644 (file)
@@ -277,7 +277,7 @@ static int multiboot_exec ( struct image *image ) {
        /* Multiboot images may not return and have no callback
         * interface, so shut everything down prior to booting the OS.
         */
-       shutdown();
+       shutdown ( SHUTDOWN_BOOT );
 
        /* Jump to OS with flat physical addressing */
        __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" )
index 73791be..e6a0ab0 100644 (file)
@@ -429,7 +429,7 @@ static int nbi_exec ( struct image *image ) {
        /* Shut down now if NBI image will not return */
        may_return = NBI_PROGRAM_RETURNS ( imgheader.flags );
        if ( ! may_return )
-               shutdown();
+               shutdown ( SHUTDOWN_BOOT );
 
        /* Execute NBI image */
        if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) {
index 4202682..220b7cd 100644 (file)
@@ -205,3 +205,10 @@ REQUIRE_OBJECT ( gdbidt );
 REQUIRE_OBJECT ( gdbudp );
 REQUIRE_OBJECT ( gdbstub_cmd );
 #endif
+
+/*
+ * Drag in objects that are always required, but not dragged in via
+ * symbol dependencies.
+ *
+ */
+REQUIRE_OBJECT ( device );
index b1b148e..84915c2 100644 (file)
@@ -20,6 +20,7 @@
 #include <gpxe/list.h>
 #include <gpxe/tables.h>
 #include <gpxe/device.h>
+#include <gpxe/init.h>
 
 /**
  * @file
@@ -68,13 +69,11 @@ static void rootdev_remove ( struct root_device *rootdev ) {
 /**
  * Probe all devices
  *
- * @ret rc             Return status code
- *
  * This initiates probing for all devices in the system.  After this
  * call, the device hierarchy will be populated, and all hardware
  * should be ready to use.
  */
-int probe_devices ( void ) {
+static void probe_devices ( void ) {
        struct root_device *rootdev;
        int rc;
 
@@ -84,19 +83,28 @@ int probe_devices ( void ) {
                if ( ( rc = rootdev_probe ( rootdev ) ) != 0 )
                        list_del ( &rootdev->dev.siblings );
        }
-       return 0;
 }
 
 /**
  * Remove all devices
  *
  */
-void remove_devices ( void ) {
+static void remove_devices ( int flags ) {
        struct root_device *rootdev;
        struct root_device *tmp;
 
+       if ( flags & SHUTDOWN_KEEP_DEVICES ) {
+               DBG ( "Refusing to remove devices on shutdown\n" );
+               return;
+       }
+
        list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) {
                rootdev_remove ( rootdev );
                list_del ( &rootdev->dev.siblings );
        }
 }
+
+struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = {
+       .startup = probe_devices,
+       .shutdown = remove_devices,
+};
index ed91bf3..50e199c 100644 (file)
@@ -79,17 +79,14 @@ void startup ( void ) {
                        startup_fn->startup();
        }
 
-       /* Probe for all devices.  Treated separately because nothing
-        * else will drag in device.o
-        */
-       probe_devices();
-
        started = 1;
 }
 
 /**
  * Shut down gPXE
  *
+ * @v flags            Shutdown behaviour flags
+ *
  * This function reverses the actions of startup(), and leaves gPXE in
  * a state ready to be removed from memory.  You may call startup()
  * again after calling shutdown().
@@ -97,20 +94,17 @@ void startup ( void ) {
  * Call this function only once, before either exiting main() or
  * starting up a non-returnable image.
  */
-void shutdown ( void ) {
+void shutdown ( int flags ) {
        struct startup_fn *startup_fn;
 
        if ( ! started )
                return;
 
-       /* Remove all devices */
-       remove_devices();
-
        /* Call registered shutdown functions (in reverse order) */
        for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ;
              startup_fn-- ) {
                if ( startup_fn->shutdown )
-                       startup_fn->shutdown();
+                       startup_fn->shutdown ( flags );
        }
 
        started = 0;
index ca62db2..d589226 100644 (file)
@@ -62,7 +62,7 @@ __cdecl int main ( void ) {
                        shell();
        }
 
-       shutdown();
+       shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );
 
        return 0;
 }
index 54c2295..97640f9 100644 (file)
@@ -224,7 +224,7 @@ static void serial_init ( void ) {
  *     Cleanup our use of the serial port, in particular flush the
  *     output buffer so we don't accidentially lose characters.
  */
-static void serial_fini ( void ) {
+static void serial_fini ( int flags __unused ) {
        int i, status;
        /* Flush the output buffer to avoid dropping characters,
         * if we are reinitializing the serial port.
@@ -247,6 +247,6 @@ struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = {
 };
 
 /** Serial driver startup function */
-struct startup_fn serial_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = {
        .shutdown = serial_fini,
 };
index caabdae..f40cc95 100644 (file)
@@ -105,7 +105,4 @@ struct root_driver {
 /** Declare a root device */
 #define __root_device __table ( struct root_device, root_devices, 01 )
 
-extern int probe_devices ( void );
-extern void remove_devices ( void );
-
 #endif /* _GPXE_DEVICE_H */
index 6144597..1c9c49f 100644 (file)
@@ -164,7 +164,7 @@ struct dhcp_packet;
  * priority of multiple option blocks (e.g. options from non-volatile
  * storage versus options from a DHCP server).
  */
-#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 )
+#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x01 )
 
 /** "Your" IP address
  *
@@ -172,7 +172,7 @@ struct dhcp_packet;
  * field, in order to provide a consistent approach to storing and
  * processing options.  It should never be present in a DHCP packet.
  */
-#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 2 )
+#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x02 )
 
 /** "Server" IP address
  *
@@ -180,7 +180,16 @@ struct dhcp_packet;
  * field, in order to provide a consistent approach to storing and
  * processing options.  It should never be present in a DHCP packet.
  */
-#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
+#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x03 )
+
+/** Keep SAN drive registered
+ *
+ * If set to a non-zero value, gPXE will not detach any SAN drive
+ * after failing to boot from it.  (This option is required in order
+ * to perform a Windows Server 2008 installation direct to an iSCSI
+ * target.)
+ */
+#define DHCP_EB_KEEP_SAN DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x08 )
 
 /*
  * Tags in the range 0x10-0x7f are reserved for feature markers
index 5717bc7..ca0abeb 100644 (file)
 #define ERRFILE_ibft                 ( ERRFILE_OTHER | 0x000c0000 )
 #define ERRFILE_tls                  ( ERRFILE_OTHER | 0x000d0000 )
 #define ERRFILE_ifmgmt               ( ERRFILE_OTHER | 0x000e0000 )
+#define ERRFILE_iscsiboot            ( ERRFILE_OTHER | 0x000f0000 )
 
 /** @} */
 
index c468213..d2b450d 100644 (file)
@@ -28,6 +28,16 @@ struct init_fn {
 
 /** @} */
 
+/** Shutdown flags */
+enum shutdown_flags {
+       /** Shutdown is in order to exit (return to gPXE's caller) */
+       SHUTDOWN_EXIT = 0x0001,
+       /** Shutdown is in order to boot an OS */
+       SHUTDOWN_BOOT = 0x0002,
+       /** Do not remove devices */
+       SHUTDOWN_KEEP_DEVICES = 0x0004,
+};
+
 /**
  * A startup/shutdown function
  *
@@ -36,7 +46,7 @@ struct init_fn {
  */
 struct startup_fn {
        void ( * startup ) ( void );
-       void ( * shutdown ) ( void );
+       void ( * shutdown ) ( int flags );
 };
 
 /** Declare a startup/shutdown function */
@@ -58,6 +68,6 @@ struct startup_fn {
 
 extern void initialise ( void );
 extern void startup ( void );
-extern void shutdown ( void );
+extern void shutdown ( int flags );
 
 #endif /* _GPXE_INIT_H */
index b451a8c..b64cbb8 100644 (file)
@@ -7,6 +7,8 @@
  *
  */
 
+extern int shutdown_exit_flags;
+
 extern void autoboot ( void );
 extern int boot_root_path ( const char *root_path );
 
index b2914d3..8220d1f 100644 (file)
@@ -318,7 +318,7 @@ PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
        pxe_set_netdev ( NULL );
 
        /* Prepare for unload */
-       shutdown();
+       shutdown ( SHUTDOWN_BOOT );
 
        stop_undi->Status = PXENV_STATUS_SUCCESS;
        return PXENV_EXIT_SUCCESS;
index cff6e95..fdd502d 100644 (file)
@@ -41,6 +41,9 @@
 /** Time to wait for link-up */
 #define LINK_WAIT_MS 15000
 
+/** Shutdown flags for exit */
+int shutdown_exit_flags = 0;
+
 /**
  * Identify the boot network device
  *
index 99edc87..84d77c4 100644 (file)
@@ -1,13 +1,25 @@
 #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
  *
@@ -25,45 +37,66 @@ static struct net_device * guess_boot_netdev ( void ) {
 }
 
 int iscsiboot ( const char *root_path ) {
-       struct scsi_device scsi;
-       struct int13_drive drive;
+       struct scsi_device *scsi;
+       struct int13_drive *drive;
+       int keep_san;
        int rc;
 
-       memset ( &scsi, 0, sizeof ( scsi ) );
-       memset ( &drive, 0, sizeof ( drive ) );
+       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 ) {
+       if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) {
                printf ( "Could not attach iSCSI device: %s\n",
                         strerror ( rc ) );
-               goto error_attach;
+               goto err_attach;
        }
-       if ( ( rc = init_scsidev ( &scsi ) ) != 0 ) {
+       if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
                printf ( "Could not initialise iSCSI device: %s\n",
                         strerror ( rc ) );
-               goto error_init;
+               goto err_init;
        }
 
-       drive.blockdev = &scsi.blockdev;
+       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 );
+               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 );
+       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 );
+       /* 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 );
 
- error_init:
-       iscsi_detach ( &scsi );
- error_attach:
+ err_init:
+       iscsi_detach ( scsi );
+ err_attach:
+       free ( drive );
+ err_alloc_drive:
+       free ( scsi );
+ err_alloc_scsi:
        return rc;
 }