From dc8eb04647636b6e13c6a728506aba0071c62434 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 21 May 2010 03:10:03 +0100 Subject: [PATCH] [pxe] Treat PXENV_RESTART_TFTP as unreturnable Microsoft WDS can end up calling PXENV_RESTART_TFTP to execute a second-stage NBP which then exits. Specifically, wdsnbp.com uses PXENV_RESTART_TFTP to execute pxeboot.com, which will exit if the user does not press F12. iPXE currently treats PXENV_RESTART_TFTP as a normal PXE API call, and so attempts to return to wdsnbp.com, which has just been vaporised by pxeboot.com. Use rmsetjmp/rmlongjmp to preserve the stack state as of the initial NBP execution, and to restore this state immediately prior to executing the NBP loaded via PXENV_RESTART_TFTP. This matches the behaviour in the PXE spec (which says that "if TFTP is restarted, control is never returned to the caller"), and allows pxeboot.com to exit relatively cleanly back to iPXE. As with all usage of setjmp/longjmp, there may be subtle corner case bugs due to not gracefully unwinding any state accumulated by the time of the longjmp call, but this seems to be the only viable way to provide the specified behaviour. Signed-off-by: Michael Brown --- src/arch/i386/include/pxe_call.h | 4 ++++ src/arch/i386/interface/pxe/pxe_call.c | 10 ++++++++++ src/arch/i386/interface/pxe/pxe_preboot.c | 10 +++------- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/arch/i386/include/pxe_call.h b/src/arch/i386/include/pxe_call.h index 4d245616..e6f6605d 100644 --- a/src/arch/i386/include/pxe_call.h +++ b/src/arch/i386/include/pxe_call.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include struct net_device; @@ -30,6 +31,9 @@ extern struct s_PXE __text16 ( ppxe ); extern struct s_PXENV __text16 ( pxenv ); #define pxenv __use_text16 ( pxenv ) +/** PXENV_RESTART_TFTP jump buffer */ +extern rmjmp_buf pxe_restart_nbp; + extern void pxe_activate ( struct net_device *netdev ); extern int pxe_deactivate ( void ); extern int pxe_start_nbp ( void ); diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index f6b3fc54..2b17f70e 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include #include @@ -480,15 +481,24 @@ int pxe_deactivate ( void ) { return 0; } +/** Jump buffer for PXENV_RESTART_TFTP */ +rmjmp_buf pxe_restart_nbp; + /** * Start PXE NBP at 0000:7c00 * * @ret rc Return status code */ int pxe_start_nbp ( void ) { + int jmp; int discard_b, discard_c, discard_d, discard_D; uint16_t rc; + /* Allow restarting NBP via PXENV_RESTART_TFTP */ + jmp = rmsetjmp ( pxe_restart_nbp ); + if ( jmp ) + DBG ( "Restarting NBP (%x)\n", jmp ); + /* Far call to PXE NBP */ __asm__ __volatile__ ( REAL_CODE ( "movw %%cx, %%es\n\t" "pushw %%es\n\t" diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/i386/interface/pxe/pxe_preboot.c index 1d36faea..7698df52 100644 --- a/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/src/arch/i386/interface/pxe/pxe_preboot.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -227,13 +228,8 @@ PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit; - /* Fire up the new NBP */ - restart_tftp->Status = pxe_start_nbp(); - - /* Not sure what "SUCCESS" actually means, since we can only - * return if the new NBP failed to boot... - */ - return PXENV_EXIT_SUCCESS; + /* Restart NBP */ + rmlongjmp ( pxe_restart_nbp, PXENV_RESTART_TFTP ); } /* PXENV_START_UNDI