[comboot] Allow for tail recursion of COMBOOT images
Multi-level menus via COMBOOT rely on the COMBOOT program being able to exit and invoke a new COMBOOT program (the next menu). This works, but rapidly (within about five iterations) runs out of space in gPXE's internal stack, since each new image is executed in a new function context. Fix by allowing tail recursion between images; an image can now specify a replacement image for itself, and image_exec() will perform the necessary tail recursion.
This commit is contained in:
parent
349868b8fd
commit
8904cd55f1
|
@ -70,7 +70,7 @@ static int com32_exec ( struct image *image ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
|
DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
|
||||||
image, (int)avail_mem_top );
|
image, avail_mem_top );
|
||||||
|
|
||||||
assert ( avail_mem_top != 0 );
|
assert ( avail_mem_top != 0 );
|
||||||
|
|
||||||
|
@ -79,11 +79,11 @@ static int com32_exec ( struct image *image ) {
|
||||||
/* Hook COMBOOT API interrupts */
|
/* Hook COMBOOT API interrupts */
|
||||||
hook_comboot_interrupts();
|
hook_comboot_interrupts();
|
||||||
|
|
||||||
/* Temporarily de-register image, so that a "boot" command
|
/* Unregister image, so that a "boot" command doesn't
|
||||||
* doesn't throw us into an execution loop. Hold a reference
|
* throw us into an execution loop. We never
|
||||||
* to avoid the image's being freed.
|
* reregister ourselves; COMBOOT images expect to be
|
||||||
|
* removed on exit.
|
||||||
*/
|
*/
|
||||||
image_get ( image );
|
|
||||||
unregister_image ( image );
|
unregister_image ( image );
|
||||||
|
|
||||||
__asm__ __volatile__ (
|
__asm__ __volatile__ (
|
||||||
|
@ -111,26 +111,32 @@ static int com32_exec ( struct image *image ) {
|
||||||
/* %6 */ "r" ( COM32_START_PHYS )
|
/* %6 */ "r" ( COM32_START_PHYS )
|
||||||
:
|
:
|
||||||
"memory" );
|
"memory" );
|
||||||
|
DBGC ( image, "COM32 %p: returned\n", image );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBOOT_RETURN_RUN_KERNEL:
|
case COMBOOT_EXIT:
|
||||||
DBGC ( image, "COM32 %p: returned to run kernel...\n", image );
|
DBGC ( image, "COM32 %p: exited\n", image );
|
||||||
comboot_run_kernel ( );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBOOT_RETURN_EXIT:
|
case COMBOOT_EXIT_RUN_KERNEL:
|
||||||
|
DBGC ( image, "COM32 %p: exited to run kernel %p\n",
|
||||||
|
image, comboot_replacement_image );
|
||||||
|
image->replacement = comboot_replacement_image;
|
||||||
|
image_autoload ( image->replacement );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case COMBOOT_EXIT_COMMAND:
|
||||||
|
DBGC ( image, "COM32 %p: exited after executing command\n",
|
||||||
|
image );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert ( 0 );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
comboot_force_text_mode();
|
comboot_force_text_mode();
|
||||||
|
|
||||||
DBGC ( image, "COM32 %p returned\n", image );
|
|
||||||
|
|
||||||
/* Re-register image and return */
|
|
||||||
register_image ( image );
|
|
||||||
image_put ( image );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,11 +147,11 @@ static int comboot_exec ( struct image *image ) {
|
||||||
DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
|
DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
|
||||||
COMBOOT_PSP_SEG );
|
COMBOOT_PSP_SEG );
|
||||||
|
|
||||||
/* Temporarily de-register image, so that a "boot" command
|
/* Unregister image, so that a "boot" command doesn't
|
||||||
* doesn't throw us into an execution loop. Hold a reference
|
* throw us into an execution loop. We never
|
||||||
* to avoid the image's being freed.
|
* reregister ourselves; COMBOOT images expect to be
|
||||||
|
* removed on exit.
|
||||||
*/
|
*/
|
||||||
image_get ( image );
|
|
||||||
unregister_image ( image );
|
unregister_image ( image );
|
||||||
|
|
||||||
/* Store stack segment at 0x38 and stack pointer at 0x3A
|
/* Store stack segment at 0x38 and stack pointer at 0x3A
|
||||||
|
@ -180,26 +180,32 @@ static int comboot_exec ( struct image *image ) {
|
||||||
"xorw %%bp, %%bp\n\t"
|
"xorw %%bp, %%bp\n\t"
|
||||||
"lret\n\t" )
|
"lret\n\t" )
|
||||||
: : "r" ( COMBOOT_PSP_SEG ) : "eax" );
|
: : "r" ( COMBOOT_PSP_SEG ) : "eax" );
|
||||||
|
DBGC ( image, "COMBOOT %p: returned\n", image );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBOOT_RETURN_RUN_KERNEL:
|
case COMBOOT_EXIT:
|
||||||
DBGC ( image, "COMBOOT %p: returned to run kernel...\n", image );
|
DBGC ( image, "COMBOOT %p: exited\n", image );
|
||||||
comboot_run_kernel ( );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBOOT_RETURN_EXIT:
|
case COMBOOT_EXIT_RUN_KERNEL:
|
||||||
|
DBGC ( image, "COMBOOT %p: exited to run kernel %p\n",
|
||||||
|
image, comboot_replacement_image );
|
||||||
|
image->replacement = comboot_replacement_image;
|
||||||
|
image_autoload ( image->replacement );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case COMBOOT_EXIT_COMMAND:
|
||||||
|
DBGC ( image, "COMBOOT %p: exited after executing command\n",
|
||||||
|
image );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert ( 0 );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
comboot_force_text_mode();
|
comboot_force_text_mode();
|
||||||
|
|
||||||
DBGC ( image, "COMBOOT %p returned\n", image );
|
|
||||||
|
|
||||||
/* Re-register image and return */
|
|
||||||
register_image ( image );
|
|
||||||
image_put ( image );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define ERRFILE_comboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00070000 )
|
#define ERRFILE_comboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00070000 )
|
||||||
#define ERRFILE_com32 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00080000 )
|
#define ERRFILE_com32 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00080000 )
|
||||||
#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 )
|
#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 )
|
||||||
|
#define ERRFILE_comboot_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000a0000 )
|
||||||
|
|
||||||
#define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 )
|
#define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 )
|
||||||
#define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 )
|
#define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 )
|
||||||
|
|
|
@ -79,18 +79,14 @@ extern int comboot_resolv ( const char *name, struct in_addr *address );
|
||||||
/* setjmp/longjmp context buffer used to return after loading an image */
|
/* setjmp/longjmp context buffer used to return after loading an image */
|
||||||
extern jmp_buf comboot_return;
|
extern jmp_buf comboot_return;
|
||||||
|
|
||||||
/* Command line to execute when returning via comboot_return
|
/* Replacement image when exiting with COMBOOT_EXIT_RUN_KERNEL */
|
||||||
* with COMBOOT_RETURN_RUN_KERNEL
|
extern struct image *comboot_replacement_image;
|
||||||
*/
|
|
||||||
extern char *comboot_kernel_cmdline;
|
|
||||||
|
|
||||||
/* Execute comboot_image_cmdline */
|
|
||||||
extern void comboot_run_kernel ( );
|
|
||||||
|
|
||||||
extern void *com32_external_esp;
|
extern void *com32_external_esp;
|
||||||
|
|
||||||
#define COMBOOT_RETURN_EXIT 1
|
#define COMBOOT_EXIT 1
|
||||||
#define COMBOOT_RETURN_RUN_KERNEL 2
|
#define COMBOOT_EXIT_RUN_KERNEL 2
|
||||||
|
#define COMBOOT_EXIT_COMMAND 3
|
||||||
|
|
||||||
extern void comboot_force_text_mode ( void );
|
extern void comboot_force_text_mode ( void );
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
#include <gpxe/process.h>
|
#include <gpxe/process.h>
|
||||||
#include <gpxe/serial.h>
|
#include <gpxe/serial.h>
|
||||||
#include <gpxe/init.h>
|
#include <gpxe/init.h>
|
||||||
|
#include <gpxe/image.h>
|
||||||
|
#include <usr/imgmgmt.h>
|
||||||
|
|
||||||
/** The "SYSLINUX" version string */
|
/** The "SYSLINUX" version string */
|
||||||
static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION;
|
static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION;
|
||||||
|
@ -67,10 +69,8 @@ extern void int22_wrapper ( void );
|
||||||
/* setjmp/longjmp context buffer used to return after loading an image */
|
/* setjmp/longjmp context buffer used to return after loading an image */
|
||||||
jmp_buf comboot_return;
|
jmp_buf comboot_return;
|
||||||
|
|
||||||
/* Command line to execute when returning via comboot_return
|
/* Replacement image when exiting with COMBOOT_EXIT_RUN_KERNEL */
|
||||||
* with COMBOOT_RETURN_RUN_KERNEL
|
struct image *comboot_replacement_image;
|
||||||
*/
|
|
||||||
char *comboot_kernel_cmdline;
|
|
||||||
|
|
||||||
/* Mode flags set by INT 22h AX=0017h */
|
/* Mode flags set by INT 22h AX=0017h */
|
||||||
static uint16_t comboot_graphics_mode = 0;
|
static uint16_t comboot_graphics_mode = 0;
|
||||||
|
@ -154,58 +154,81 @@ void comboot_force_text_mode ( void ) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the kernel specified in comboot_kernel_cmdline
|
* Fetch kernel and optional initrd
|
||||||
*/
|
*/
|
||||||
void comboot_run_kernel ( )
|
static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) {
|
||||||
{
|
struct image *kernel;
|
||||||
char *initrd;
|
struct image *initrd = NULL;
|
||||||
|
char *initrd_file;
|
||||||
comboot_force_text_mode ( );
|
int rc;
|
||||||
|
|
||||||
DBG ( "COMBOOT: executing image '%s'\n", comboot_kernel_cmdline );
|
|
||||||
|
|
||||||
/* Find initrd= parameter, if any */
|
/* Find initrd= parameter, if any */
|
||||||
if ( ( initrd = strstr ( comboot_kernel_cmdline, "initrd=" ) ) ) {
|
if ( ( initrd_file = strstr ( cmdline, "initrd=" ) ) != NULL ) {
|
||||||
char old_char = '\0';
|
char *initrd_end;
|
||||||
char *initrd_end = strchr( initrd, ' ' );
|
|
||||||
|
|
||||||
/* Replace space after end of parameter
|
/* skip "initrd=" */
|
||||||
* with a nul terminator if this is not
|
initrd_file += 7;
|
||||||
* the last parameter
|
|
||||||
*/
|
/* Find terminating space, if any, and replace with NUL */
|
||||||
if ( initrd_end ) {
|
initrd_end = strchr ( initrd_file, ' ' );
|
||||||
old_char = *initrd_end;
|
if ( initrd_end )
|
||||||
*initrd_end = '\0';
|
*initrd_end = '\0';
|
||||||
|
|
||||||
|
DBG ( "COMBOOT: fetching initrd '%s'\n", initrd_file );
|
||||||
|
|
||||||
|
/* Allocate and fetch initrd */
|
||||||
|
initrd = alloc_image();
|
||||||
|
if ( ! initrd ) {
|
||||||
|
DBG ( "COMBOOT: could not allocate initrd\n" );
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc_initrd;
|
||||||
|
}
|
||||||
|
if ( ( rc = imgfetch ( initrd, initrd_file,
|
||||||
|
register_image ) ) != 0 ) {
|
||||||
|
DBG ( "COMBOOT: could not fetch initrd: %s\n",
|
||||||
|
strerror ( rc ) );
|
||||||
|
goto err_fetch_initrd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace = with space to get 'initrd filename'
|
/* Restore space after initrd name, if applicable */
|
||||||
* command suitable for system()
|
if ( initrd_end )
|
||||||
*/
|
*initrd_end = ' ';
|
||||||
initrd[6] = ' ';
|
|
||||||
|
|
||||||
DBG( "COMBOOT: loading initrd '%s'\n", initrd );
|
|
||||||
|
|
||||||
system ( initrd );
|
|
||||||
|
|
||||||
/* Restore space after parameter */
|
|
||||||
if ( initrd_end ) {
|
|
||||||
*initrd_end = old_char;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restore = */
|
DBG ( "COMBOOT: fetching kernel '%s'\n", kernel_file );
|
||||||
initrd[6] = '=';
|
|
||||||
|
/* Allocate and fetch kernel */
|
||||||
|
kernel = alloc_image();
|
||||||
|
if ( ! kernel ) {
|
||||||
|
DBG ( "COMBOOT: could not allocate kernel\n" );
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto err_alloc_kernel;
|
||||||
|
}
|
||||||
|
if ( ( rc = imgfetch ( kernel, kernel_file,
|
||||||
|
register_image ) ) != 0 ) {
|
||||||
|
DBG ( "COMBOOT: could not fetch kernel: %s\n",
|
||||||
|
strerror ( rc ) );
|
||||||
|
goto err_fetch_kernel;
|
||||||
|
}
|
||||||
|
if ( ( rc = image_set_cmdline ( kernel, cmdline ) ) != 0 ) {
|
||||||
|
DBG ( "COMBOOT: could not set kernel command line: %s\n",
|
||||||
|
strerror ( rc ) );
|
||||||
|
goto err_set_cmdline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load kernel */
|
/* Store kernel as replacement image */
|
||||||
DBG ( "COMBOOT: loading kernel '%s'\n", comboot_kernel_cmdline );
|
comboot_replacement_image = kernel;
|
||||||
system ( comboot_kernel_cmdline );
|
|
||||||
|
|
||||||
free ( comboot_kernel_cmdline );
|
return 0;
|
||||||
|
|
||||||
/* Boot */
|
err_set_cmdline:
|
||||||
system ( "boot" );
|
err_fetch_kernel:
|
||||||
|
image_put ( kernel );
|
||||||
DBG ( "COMBOOT: back from executing command\n" );
|
err_alloc_kernel:
|
||||||
|
err_fetch_initrd:
|
||||||
|
image_put ( initrd );
|
||||||
|
err_alloc_initrd:
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,7 +236,7 @@ void comboot_run_kernel ( )
|
||||||
* Terminate program interrupt handler
|
* Terminate program interrupt handler
|
||||||
*/
|
*/
|
||||||
static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) {
|
static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) {
|
||||||
longjmp ( comboot_return, COMBOOT_RETURN_EXIT );
|
longjmp ( comboot_return, COMBOOT_EXIT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,7 +249,7 @@ static __asmcall void int21 ( struct i386_all_regs *ix86 ) {
|
||||||
switch ( ix86->regs.ah ) {
|
switch ( ix86->regs.ah ) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
case 0x4C: /* Terminate program */
|
case 0x4C: /* Terminate program */
|
||||||
longjmp ( comboot_return, COMBOOT_RETURN_EXIT );
|
longjmp ( comboot_return, COMBOOT_EXIT );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x01: /* Get Key with Echo */
|
case 0x01: /* Get Key with Echo */
|
||||||
|
@ -323,17 +346,15 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
|
||||||
char cmd[len + 1];
|
char cmd[len + 1];
|
||||||
copy_from_user ( cmd, cmd_u, 0, len + 1 );
|
copy_from_user ( cmd, cmd_u, 0, len + 1 );
|
||||||
DBG ( "COMBOOT: executing command '%s'\n", cmd );
|
DBG ( "COMBOOT: executing command '%s'\n", cmd );
|
||||||
|
system ( cmd );
|
||||||
comboot_kernel_cmdline = strdup ( cmd );
|
DBG ( "COMBOOT: exiting after executing command...\n" );
|
||||||
|
longjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
|
||||||
DBG ( "COMBOOT: returning to run image...\n" );
|
|
||||||
longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0004: /* Run default command */
|
case 0x0004: /* Run default command */
|
||||||
/* FIXME: just exit for now */
|
/* FIXME: just exit for now */
|
||||||
longjmp ( comboot_return, COMBOOT_RETURN_EXIT );
|
longjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0005: /* Force text mode */
|
case 0x0005: /* Force text mode */
|
||||||
|
@ -518,21 +539,21 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
|
||||||
userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
|
userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
|
||||||
int file_len = strlen_user ( file_u, 0 );
|
int file_len = strlen_user ( file_u, 0 );
|
||||||
int cmd_len = strlen_user ( cmd_u, 0 );
|
int cmd_len = strlen_user ( cmd_u, 0 );
|
||||||
char file[file_len + 1 + cmd_len + 7 + 1];
|
char file[file_len + 1];
|
||||||
char cmd[cmd_len + 1];
|
char cmd[cmd_len + 1];
|
||||||
|
|
||||||
memcpy( file, "kernel ", 7 );
|
copy_from_user ( file, file_u, 0, file_len + 1 );
|
||||||
copy_from_user ( file + 7, file_u, 0, file_len + 1 );
|
|
||||||
copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 );
|
copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 );
|
||||||
strcat ( file, " " );
|
|
||||||
strcat ( file, cmd );
|
|
||||||
|
|
||||||
DBG ( "COMBOOT: run kernel image '%s'\n", file );
|
DBG ( "COMBOOT: run kernel %s %s\n", file, cmd );
|
||||||
|
comboot_fetch_kernel ( file, cmd );
|
||||||
comboot_kernel_cmdline = strdup ( file );
|
/* Technically, we should return if we
|
||||||
|
* couldn't load the kernel, but it's not safe
|
||||||
DBG ( "COMBOOT: returning to run image...\n" );
|
* to do that since we have just overwritten
|
||||||
longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL );
|
* part of the COMBOOT program's memory space.
|
||||||
|
*/
|
||||||
|
DBG ( "COMBOOT: exiting to run kernel...\n" );
|
||||||
|
longjmp ( comboot_return, COMBOOT_EXIT_RUN_KERNEL );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ static void free_image ( struct refcnt *refcnt ) {
|
||||||
|
|
||||||
uri_put ( image->uri );
|
uri_put ( image->uri );
|
||||||
ufree ( image->data );
|
ufree ( image->data );
|
||||||
|
image_put ( image->replacement );
|
||||||
free ( image );
|
free ( image );
|
||||||
DBGC ( image, "IMAGE %p freed\n", image );
|
DBGC ( image, "IMAGE %p freed\n", image );
|
||||||
}
|
}
|
||||||
|
@ -142,9 +143,9 @@ int register_image ( struct image *image ) {
|
||||||
* @v image Executable/loadable image
|
* @v image Executable/loadable image
|
||||||
*/
|
*/
|
||||||
void unregister_image ( struct image *image ) {
|
void unregister_image ( struct image *image ) {
|
||||||
|
DBGC ( image, "IMAGE %p unregistered\n", image );
|
||||||
list_del ( &image->list );
|
list_del ( &image->list );
|
||||||
image_put ( image );
|
image_put ( image );
|
||||||
DBGC ( image, "IMAGE %p unregistered\n", image );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,6 +238,7 @@ int image_autoload ( struct image *image ) {
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
int image_exec ( struct image *image ) {
|
int image_exec ( struct image *image ) {
|
||||||
|
struct image *replacement;
|
||||||
struct uri *old_cwuri;
|
struct uri *old_cwuri;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -257,18 +259,40 @@ int image_exec ( struct image *image ) {
|
||||||
old_cwuri = uri_get ( cwuri );
|
old_cwuri = uri_get ( cwuri );
|
||||||
churi ( image->uri );
|
churi ( image->uri );
|
||||||
|
|
||||||
|
/* Take out a temporary reference to the image. This allows
|
||||||
|
* the image to unregister itself if necessary, without
|
||||||
|
* automatically freeing itself.
|
||||||
|
*/
|
||||||
|
image_get ( image );
|
||||||
|
|
||||||
/* Try executing the image */
|
/* Try executing the image */
|
||||||
if ( ( rc = image->type->exec ( image ) ) != 0 ) {
|
if ( ( rc = image->type->exec ( image ) ) != 0 ) {
|
||||||
DBGC ( image, "IMAGE %p could not execute: %s\n",
|
DBGC ( image, "IMAGE %p could not execute: %s\n",
|
||||||
image, strerror ( rc ) );
|
image, strerror ( rc ) );
|
||||||
goto done;
|
/* Do not return yet; we still have clean-up to do */
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
/* Pick up replacement image before we drop the original
|
||||||
|
* image's temporary reference.
|
||||||
|
*/
|
||||||
|
if ( ( replacement = image->replacement ) != NULL )
|
||||||
|
image_get ( replacement );
|
||||||
|
|
||||||
|
/* Drop temporary reference to the original image */
|
||||||
|
image_put ( image );
|
||||||
|
|
||||||
/* Reset current working directory */
|
/* Reset current working directory */
|
||||||
churi ( old_cwuri );
|
churi ( old_cwuri );
|
||||||
uri_put ( old_cwuri );
|
uri_put ( old_cwuri );
|
||||||
|
|
||||||
|
/* Tail-recurse into replacement image, if one exists */
|
||||||
|
if ( replacement ) {
|
||||||
|
DBGC ( image, "IMAGE %p replacing self with IMAGE %p\n",
|
||||||
|
image, replacement );
|
||||||
|
if ( ( rc = image_exec ( replacement ) ) != 0 )
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,8 @@ static int script_exec ( struct image *image ) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Temporarily de-register image, so that a "boot" command
|
/* Temporarily de-register image, so that a "boot" command
|
||||||
* doesn't throw us into an execution loop. Hold a reference
|
* doesn't throw us into an execution loop.
|
||||||
* to avoid the image's being freed.
|
|
||||||
*/
|
*/
|
||||||
image_get ( image );
|
|
||||||
unregister_image ( image );
|
unregister_image ( image );
|
||||||
|
|
||||||
while ( offset < image->len ) {
|
while ( offset < image->len ) {
|
||||||
|
@ -80,7 +78,6 @@ static int script_exec ( struct image *image ) {
|
||||||
done:
|
done:
|
||||||
/* Re-register image and return */
|
/* Re-register image and return */
|
||||||
register_image ( image );
|
register_image ( image );
|
||||||
image_put ( image );
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,16 @@ struct image {
|
||||||
userptr_t user;
|
userptr_t user;
|
||||||
unsigned long ul;
|
unsigned long ul;
|
||||||
} priv;
|
} priv;
|
||||||
|
|
||||||
|
/** Replacement image
|
||||||
|
*
|
||||||
|
* An image wishing to replace itself with another image (in a
|
||||||
|
* style similar to a Unix exec() call) should return from its
|
||||||
|
* exec() method with the replacement image set to point to
|
||||||
|
* the new image. The new image must already be in a suitable
|
||||||
|
* state for execution.
|
||||||
|
*/
|
||||||
|
struct image *replacement;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Image is loaded */
|
/** Image is loaded */
|
||||||
|
@ -79,6 +89,10 @@ struct image_type {
|
||||||
*
|
*
|
||||||
* @v image Loaded image
|
* @v image Loaded image
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
|
*
|
||||||
|
* Note that the image may be invalidated by the act of
|
||||||
|
* execution, i.e. an image is allowed to choose to unregister
|
||||||
|
* (and so potentially free) itself.
|
||||||
*/
|
*/
|
||||||
int ( * exec ) ( struct image *image );
|
int ( * exec ) ( struct image *image );
|
||||||
};
|
};
|
||||||
|
|
Reference in New Issue