diff options
author | Michael Brown <mcb30@etherboot.org> | 2009-02-17 00:47:35 +0000 |
---|---|---|
committer | Michael Brown <mcb30@etherboot.org> | 2009-02-17 00:47:35 +0000 |
commit | 8904cd55f128941d53d9a8beef71fb32a920a92d (patch) | |
tree | a2ce11209520f09931d23ccae2682297ada99288 /src/core/image.c | |
parent | 349868b8fdd043c19956594bd144b66395523f02 (diff) | |
download | ipxe-8904cd55f128941d53d9a8beef71fb32a920a92d.tar.gz |
[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.
Diffstat (limited to 'src/core/image.c')
-rw-r--r-- | src/core/image.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/src/core/image.c b/src/core/image.c index 440a68c9..741b0547 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -53,6 +53,7 @@ static void free_image ( struct refcnt *refcnt ) { uri_put ( image->uri ); ufree ( image->data ); + image_put ( image->replacement ); free ( image ); DBGC ( image, "IMAGE %p freed\n", image ); } @@ -142,9 +143,9 @@ int register_image ( struct image *image ) { * @v image Executable/loadable image */ void unregister_image ( struct image *image ) { + DBGC ( image, "IMAGE %p unregistered\n", image ); list_del ( &image->list ); 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 */ int image_exec ( struct image *image ) { + struct image *replacement; struct uri *old_cwuri; int rc; @@ -257,18 +259,40 @@ int image_exec ( struct image *image ) { old_cwuri = uri_get ( cwuri ); 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 */ if ( ( rc = image->type->exec ( image ) ) != 0 ) { DBGC ( image, "IMAGE %p could not execute: %s\n", 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 */ churi ( 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; } |