]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[iSCSI] Support Windows Server 2008 direct iSCSI installation
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 eba94007cd42dd5c9c0c7e5a14e65eab9957879b..11ca31283c86b2857834dd011770a6f6eaf2f183 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 d9c054532e00f0670370c366539f1f437791e529..e6fd854f702e277d161c1730252baf60f650513c 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 52510aa97b6142a7c0df2f3b3d30fb4cb8f92232..60c54992adab180d214987532ec8a79d6dbe329b 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 fbaebd5c7a79f250b70e1f0a4bf4cf1e82d9946a..d7c2b8dab1edfcd919ab3f4c481eb9d7cc5b008f 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 73791be9034f6a5a3b0332528fba0686f9660b65..e6a0ab0f34021b7915b461e94f679dba8fea5d55 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 42026827609e562e1cf17473c7ebe73fb549ce39..220b7cdb12f7451184b57bfc16a969097a32fc6d 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 b1b148e853746765e17d0d906902a6a2e6c35666..84915c2daf153f136d86d2d45c09bbf5a57e4ad8 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 ed91bf367dbdeb4c9e4d0be250dac29e600273ac..50e199cec4539e365b00d13e58e27629c25ffe6a 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 ca62db25257bb3a007e067850e14d658bb16bc29..d58922617aca5a6c54c518af3b5e12ce48ef300e 100644 (file)
@@ -62,7 +62,7 @@ __cdecl int main ( void ) {
                        shell();
        }
 
-       shutdown();
+       shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );
 
        return 0;
 }
index 54c229549dcd5686c29f662914d656f48449475b..97640f9317427a1e85dced2ad29c117ccce455f7 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 caabdae5612dce54e56f8addb7d3437312d725e6..f40cc95a274dcc0a138faa9b2c73e6485e4b187b 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 6144597730b0c07780c1e78b9cccaf2da08a3b1e..1c9c49faa40d0577f18709ac7fcfbf6fa85bb82d 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 5717bc717a6692bdf552aa2cfea78dcb62fee77f..ca0abebfa70242ed2260f18c2a045063762db7ae 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 c468213e16c11627578e302320e4b760895f57ff..d2b450d7b41004eebaef6f9658d5333fa8c08f54 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 b451a8c1c452c8da94baa9467481d1ca4fd680b7..b64cbb8eef0683a1b71a86e07ee1acf131431d8c 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 b2914d366f904da74d6739724f7bf2f6c7a68bc2..8220d1f20299fb93e22de040dd3db40cd6bfc265 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 cff6e95dd817979fbbba9b5368fa6fc64342d47a..fdd502da208f56f2d92c78f043404e0c5f521ce3 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 99edc8795846193edd9f35d19dc513b049b57c5b..84d77c458dc9cd17406979fe7021039c6157109e 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;
 }