]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pxeprefix] Add .kkpxe image type and ability to return via PXE stack
authorMichael Brown <mcb30@etherboot.org>
Mon, 27 Oct 2008 01:06:00 +0000 (01:06 +0000)
committerMichael Brown <mcb30@etherboot.org>
Wed, 18 Feb 2009 18:12:32 +0000 (18:12 +0000)
Certain combinations of PXE stack and BIOS result in a broken INT 18
call, which will leave the system displaying a "PRESS ANY KEY TO
REBOOT" message instead of proceeding to the next boot device.  On
these systems, returning via the PXE stack is the only way to continue
to the next boot device.  Returning via the PXE stack works only if we
haven't already blown away the PXE base code in pxeprefix.S.

In most circumstances, we do want to blow away the PXE base code.
Base memory is a limited resource, and it is desirable to reclaim as
much as possible.  When we perform an iSCSI boot, we need to place the
iBFT above the 512kB mark, because otherwise it may not be detected by
the loaded OS; this may not be possible if the PXE base code is still
occupying that memory.

Introduce a new prefix type .kkpxe which will preserve both the PXE
base code and the UNDI driver (as compared to .kpxe, which preserves
the UNDI driver but uninstalls the PXE base code).  This prefix type
can be used on systems that are known to experience the specific
problem of INT 18 being broken, or in builds (such as gpxelinux.0) for
which it is particularly important to know that returning to the BIOS
will work.

Written by H. Peter Anvin <hpa@zytor.com> and Stefan Hajnoczi
<stefanha@gmail.com>, minor structural alterations by Michael Brown
<mcb30@etherboot.org>.

src/arch/i386/Makefile.pcbios
src/arch/i386/drivers/net/undinet.c
src/arch/i386/drivers/net/undionly.c
src/arch/i386/include/undi.h
src/arch/i386/prefix/kkpxeprefix.S [new file with mode: 0644]
src/arch/i386/prefix/pxeprefix.S
src/include/gpxe/init.h

index a93861abde38a76284449404c5695d2eee94d6f0..64b3dac28c876864181d4d95b56389669419b2c7 100644 (file)
@@ -13,6 +13,7 @@ LDFLAGS               += -N --no-check-sections
 MEDIA          += rom
 MEDIA          += pxe
 MEDIA          += kpxe
+MEDIA          += kkpxe
 MEDIA          += elf
 MEDIA          += elfd
 MEDIA          += lmelf
index 7ebfd3c4deb96d84bd039f1d277a4a5079f18592..d6db6f7c15e9f4b53e4ceed93f43ae8f0ab91479 100644 (file)
@@ -669,15 +669,19 @@ int undinet_probe ( struct undi_device *undi ) {
        undi->flags |= UNDI_FL_STARTED;
 
        /* Bring up UNDI stack */
-       memset ( &undi_startup, 0, sizeof ( undi_startup ) );
-       if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, &undi_startup,
-                                  sizeof ( undi_startup ) ) ) != 0 )
-               goto err_undi_startup;
-       memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
-       if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
-                                  &undi_initialize,
-                                  sizeof ( undi_initialize ) ) ) != 0 )
-               goto err_undi_initialize;
+       if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
+               memset ( &undi_startup, 0, sizeof ( undi_startup ) );
+               if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
+                                          &undi_startup,
+                                          sizeof ( undi_startup ) ) ) != 0 )
+                       goto err_undi_startup;
+               memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
+               if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
+                                          &undi_initialize,
+                                          sizeof ( undi_initialize ))) != 0 )
+                       goto err_undi_initialize;
+       }
+       undi->flags |= UNDI_FL_INITIALIZED;
 
        /* Get device information */
        memset ( &undi_info, 0, sizeof ( undi_info ) );
@@ -731,11 +735,13 @@ int undinet_probe ( struct undi_device *undi ) {
        memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
        undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
                       sizeof ( undi_cleanup ) );
+       undi->flags &= ~UNDI_FL_INITIALIZED;
  err_undi_startup:
        /* Unhook UNDI stack */
        memset ( &stop_undi, 0, sizeof ( stop_undi ) );
        undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
                       sizeof ( stop_undi ) );
+       undi->flags &= ~UNDI_FL_STARTED;
  err_start_undi:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
@@ -758,19 +764,26 @@ void undinet_remove ( struct undi_device *undi ) {
        /* Unregister net device */
        unregister_netdev ( netdev );
 
-       /* Shut down UNDI stack */
-       memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
-       undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
-                      sizeof ( undi_shutdown ) );
-       memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
-       undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
-                      sizeof ( undi_cleanup ) );
-
-       /* Unhook UNDI stack */
-       memset ( &stop_undi, 0, sizeof ( stop_undi ) );
-       undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
-                      sizeof ( stop_undi ) );
-       undi->flags &= ~UNDI_FL_STARTED;
+       /* If we are preparing for an OS boot, or if we cannot exit
+        * via the PXE stack, then shut down the PXE stack.
+        */
+       if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
+
+               /* Shut down UNDI stack */
+               memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
+               undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
+                              sizeof ( undi_shutdown ) );
+               memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
+               undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
+                              sizeof ( undi_cleanup ) );
+               undi->flags &= ~UNDI_FL_INITIALIZED;
+
+               /* Unhook UNDI stack */
+               memset ( &stop_undi, 0, sizeof ( stop_undi ) );
+               undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
+                              sizeof ( stop_undi ) );
+               undi->flags &= ~UNDI_FL_STARTED;
+       }
 
        /* Clear entry point */
        memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
index ee361493c1f238f9a7e32e21054b0311514c2856..4cdce6771ec29dca6eb6d306916aae55d7f03073 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <gpxe/device.h>
+#include <gpxe/init.h>
 #include <undi.h>
 #include <undinet.h>
 #include <undipreload.h>
@@ -107,3 +108,20 @@ struct root_device undi_root_device __root_device = {
        .dev = { .name = "UNDI" },
        .driver = &undi_root_driver,
 };
+
+/**
+ * Prepare for exit
+ *
+ * @v flags            Shutdown flags
+ */
+static void undionly_shutdown ( int flags ) {
+       /* If we are shutting down to boot an OS, clear the "keep PXE
+        * stack" flag.
+        */
+       if ( flags & SHUTDOWN_BOOT )
+               preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL;
+}
+
+struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = {
+       .shutdown = undionly_shutdown,
+};
index f6fb85fe13d461b4a6a42fff5b6a589c1a9852e3..c6253d0aeb94c1c5e45f9b6c59dd232780b39f2f 100644 (file)
@@ -95,4 +95,10 @@ static inline void * undi_get_drvdata ( struct undi_device *undi ) {
 /** UNDI flag: START_UNDI has been called */
 #define UNDI_FL_STARTED 0x0001
 
+/** UNDI flag: UNDI_STARTUP and UNDI_INITIALIZE have been called */
+#define UNDI_FL_INITIALIZED 0x0002
+
+/** UNDI flag: keep stack resident */
+#define UNDI_FL_KEEP_ALL 0x0004
+
 #endif /* _UNDI_H */
diff --git a/src/arch/i386/prefix/kkpxeprefix.S b/src/arch/i386/prefix/kkpxeprefix.S
new file mode 100644 (file)
index 0000000..e0bea0c
--- /dev/null
@@ -0,0 +1,8 @@
+/*****************************************************************************
+ * PXE prefix that keeps the whole PXE stack present
+ *****************************************************************************
+ */
+
+#define PXELOADER_KEEP_UNDI
+#define PXELOADER_KEEP_PXE
+#include "pxeprefix.S"
index 575f4784ee2c1a2e57928574c4729e42485ab392..ee0f4d948c965bc6e08d04a4575ba78ebfde1045 100644 (file)
 
 #include <undi.h>
 
+#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+
 /*****************************************************************************
  * Entry point:        set operating context, print welcome message
  *****************************************************************************
  */
        .section ".prefix", "ax", @progbits
-       /* Set up our non-stack segment registers */
        jmp     $0x7c0, $1f
-1:     movw    %cs, %ax
+1:
+       /* Preserve registers for possible return to PXE */
+       pushfl
+       pushal
+       pushw   %gs
+       pushw   %fs
+       pushw   %es
+       pushw   %ds
+
+       /* Store magic word on PXE stack and remember PXE %ss:esp */
+       pushl   $STACK_MAGIC
+       movw    %ss, %cs:pxe_ss
+       movl    %esp, %cs:pxe_esp
+       movw    %sp, %bp
+       movl    (10*4+4*2+4)(%bp),%ebp  /* !PXE address */
+
+       /* Set up %ds */
+       movw    %cs, %ax
        movw    %ax, %ds
-       movw    $0x40, %ax              /* BIOS data segment access */
-       movw    %ax, %fs
        /* Record PXENV+ and !PXE nominal addresses */
-       movw    %es, %ax                /* PXENV+ address */
-       movw    %ax, pxenv_segment
+       movw    %es, pxenv_segment      /* PXENV+ address */
        movw    %bx, pxenv_offset
-       popl    %eax                    /* Discard return address */
-       popl    ppxe_segoff             /* !PXE address */
+       movl    %ebp, ppxe_segoff       /* !PXE address */
+       /* Set up %es and %fs */
+       movw    %ax, %es
+       movw    $0x40, %ax              /* BIOS data segment access */
+       movw    %ax, %fs
        /* Set up stack just below 0x7c00 */
        xorw    %ax, %ax
        movw    %ax, %ss
-       movw    $0x7c00, %sp
+       movl    $0x7c00, %esp
        /* Clear direction flag, for the sake of sanity */
        cld
        /* Print welcome message */
@@ -249,6 +267,7 @@ no_physical_device:
  * Leave NIC in a safe state
  *****************************************************************************
  */
+#ifndef PXELOADER_KEEP_PXE
 shutdown_nic:
        /* Issue PXENV_UNDI_SHUTDOWN */
        movw    $PXENV_UNDI_SHUTDOWN, %bx
@@ -256,11 +275,6 @@ shutdown_nic:
        jnc     1f
        call    print_pxe_error
 1:
-
-/*****************************************************************************
- * Unload PXE base code
- *****************************************************************************
- */    
 unload_base_code:
        /* Issue PXENV_UNLOAD_STACK */
        movw    $PXENV_UNLOAD_STACK, %bx
@@ -273,6 +287,8 @@ unload_base_code:
        movw    %fs:(0x13), %bx
        call    free_basemem
 99:
+       andw    $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
+#endif /* PXELOADER_KEEP_PXE */
 
 /*****************************************************************************
  * Unload UNDI driver
@@ -511,6 +527,10 @@ print_pxe_error:
  * PXE data structures
  *****************************************************************************
  */
+       .section ".prefix.data"
+
+pxe_ss:                        .word 0
+pxe_esp:               .long 0
 
 pxe_parameter_structure: .fill 20
 
@@ -547,14 +567,16 @@ isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
 
 pci_vendor:            .word 0
 pci_device:            .word 0
-flags:                 .word UNDI_FL_STARTED
+flags:
+       .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
 
        .equ undi_device_size, ( . - undi_device )
 
 /*****************************************************************************
  * Run gPXE main code
  *****************************************************************************
- */    
+ */
+       .section ".prefix"
 run_gpxe:
        /* Install gPXE */
        call    install
@@ -572,6 +594,10 @@ run_gpxe:
        rep movsb
 #endif
 
+       /* Retrieve PXE %ss:esp */
+       movw    pxe_ss, %di
+       movl    pxe_esp, %ebp
+
        /* Jump to .text16 segment with %ds pointing to .data16 */
        movw    %bx, %ds
        pushw   %ax
@@ -588,6 +614,25 @@ run_gpxe:
        /* Uninstall gPXE */
        call    uninstall
 
-       /* Boot next device */
+       /* Restore PXE stack */
+       movw    %di, %ss
+       movl    %ebp, %esp
+
+       /* Check PXE stack magic */
+       popl    %eax
+       cmpl    $STACK_MAGIC, %eax
+       jne     1f
+
+       /* PXE stack OK: return to caller */
+       popw    %ds
+       popw    %es
+       popw    %fs
+       popw    %gs
+       popal
+       popfl
+       xorw    %ax, %ax        /* Return success */
+       lret
+
+1:     /* PXE stack corrupt or removed: use INT 18 */
        int     $0x18
        .previous
index d2b450d7b41004eebaef6f9658d5333fa8c08f54..e0e9f9c84cf874adcf8eb647ad113662ce96ccd2 100644 (file)
@@ -63,6 +63,7 @@ struct startup_fn {
 
 #define STARTUP_EARLY  01      /**< Early startup */
 #define STARTUP_NORMAL 02      /**< Normal startup */
+#define STARTUP_LATE   03      /**< Late startup */
 
 /** @} */