aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/arch/i386/Makefile.pcbios1
-rw-r--r--src/arch/i386/drivers/net/undinet.c57
-rw-r--r--src/arch/i386/drivers/net/undionly.c18
-rw-r--r--src/arch/i386/include/undi.h6
-rw-r--r--src/arch/i386/prefix/kkpxeprefix.S8
-rw-r--r--src/arch/i386/prefix/pxeprefix.S79
-rw-r--r--src/include/gpxe/init.h1
7 files changed, 131 insertions, 39 deletions
diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios
index a93861abd..64b3dac28 100644
--- a/src/arch/i386/Makefile.pcbios
+++ b/src/arch/i386/Makefile.pcbios
@@ -13,6 +13,7 @@ LDFLAGS += -N --no-check-sections
MEDIA += rom
MEDIA += pxe
MEDIA += kpxe
+MEDIA += kkpxe
MEDIA += elf
MEDIA += elfd
MEDIA += lmelf
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index 7ebfd3c4d..d6db6f7c1 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -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 ) );
diff --git a/src/arch/i386/drivers/net/undionly.c b/src/arch/i386/drivers/net/undionly.c
index ee361493c..4cdce6771 100644
--- a/src/arch/i386/drivers/net/undionly.c
+++ b/src/arch/i386/drivers/net/undionly.c
@@ -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,
+};
diff --git a/src/arch/i386/include/undi.h b/src/arch/i386/include/undi.h
index f6fb85fe1..c6253d0ae 100644
--- a/src/arch/i386/include/undi.h
+++ b/src/arch/i386/include/undi.h
@@ -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
index 000000000..e0bea0cdf
--- /dev/null
+++ b/src/arch/i386/prefix/kkpxeprefix.S
@@ -0,0 +1,8 @@
+/*****************************************************************************
+ * PXE prefix that keeps the whole PXE stack present
+ *****************************************************************************
+ */
+
+#define PXELOADER_KEEP_UNDI
+#define PXELOADER_KEEP_PXE
+#include "pxeprefix.S"
diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/i386/prefix/pxeprefix.S
index 575f4784e..ee0f4d948 100644
--- a/src/arch/i386/prefix/pxeprefix.S
+++ b/src/arch/i386/prefix/pxeprefix.S
@@ -10,27 +10,45 @@
#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
diff --git a/src/include/gpxe/init.h b/src/include/gpxe/init.h
index d2b450d7b..e0e9f9c84 100644
--- a/src/include/gpxe/init.h
+++ b/src/include/gpxe/init.h
@@ -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 */
/** @} */