diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios index a93861ab..64b3dac2 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 7ebfd3c4..d6db6f7c 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 ) ); + /* 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 ) ) { - /* 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; + /* 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 ee361493..4cdce677 100644 --- a/src/arch/i386/drivers/net/undionly.c +++ b/src/arch/i386/drivers/net/undionly.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -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 f6fb85fe..c6253d0a 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 00000000..e0bea0cd --- /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 575f4784..ee0f4d94 100644 --- a/src/arch/i386/prefix/pxeprefix.S +++ b/src/arch/i386/prefix/pxeprefix.S @@ -10,27 +10,45 @@ #include +#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 + /* Record PXENV+ and !PXE nominal addresses */ + movw %es, pxenv_segment /* PXENV+ address */ + movw %bx, pxenv_offset + movl %ebp, ppxe_segoff /* !PXE address */ + /* Set up %es and %fs */ + movw %ax, %es 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 %bx, pxenv_offset - popl %eax /* Discard return address */ - popl ppxe_segoff /* !PXE address */ /* 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 d2b450d7..e0e9f9c8 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 */ /** @} */