diff options
Diffstat (limited to 'contrib/syslinux/latest/gpxe/src/core/image.c')
-rw-r--r-- | contrib/syslinux/latest/gpxe/src/core/image.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/contrib/syslinux/latest/gpxe/src/core/image.c b/contrib/syslinux/latest/gpxe/src/core/image.c new file mode 100644 index 0000000..24fe51a --- /dev/null +++ b/contrib/syslinux/latest/gpxe/src/core/image.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <libgen.h> +#include <gpxe/list.h> +#include <gpxe/umalloc.h> +#include <gpxe/uri.h> +#include <gpxe/image.h> + +/** @file + * + * Executable/loadable images + * + */ + +/** List of registered images */ +struct list_head images = LIST_HEAD_INIT ( images ); + +/** + * Free executable/loadable image + * + * @v refcnt Reference counter + */ +static void free_image ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + uri_put ( image->uri ); + ufree ( image->data ); + image_put ( image->replacement ); + free ( image ); + DBGC ( image, "IMAGE %p freed\n", image ); +} + +/** + * Allocate executable/loadable image + * + * @ret image Executable/loadable image + */ +struct image * alloc_image ( void ) { + struct image *image; + + image = zalloc ( sizeof ( *image ) ); + if ( image ) { + image->refcnt.free = free_image; + } + return image; +} + +/** + * Set image URI + * + * @v image Image + * @v URI New image URI + * @ret rc Return status code + * + * If no name is set, the name will be updated to the base name of the + * URI path (if any). + */ +int image_set_uri ( struct image *image, struct uri *uri ) { + const char *path = uri->path; + + /* Replace URI reference */ + uri_put ( image->uri ); + image->uri = uri_get ( uri ); + + /* Set name if none already specified */ + if ( path && ( ! image->name[0] ) ) + image_set_name ( image, basename ( ( char * ) path ) ); + + return 0; +} + +/** + * Set image command line + * + * @v image Image + * @v cmdline New image command line + * @ret rc Return status code + */ +int image_set_cmdline ( struct image *image, const char *cmdline ) { + free ( image->cmdline ); + image->cmdline = strdup ( cmdline ); + if ( ! image->cmdline ) + return -ENOMEM; + return 0; +} + +/** + * Register executable/loadable image + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int register_image ( struct image *image ) { + static unsigned int imgindex = 0; + + /* Create image name if it doesn't already have one */ + if ( ! image->name[0] ) { + snprintf ( image->name, sizeof ( image->name ), "img%d", + imgindex++ ); + } + + /* Add to image list */ + image_get ( image ); + list_add_tail ( &image->list, &images ); + DBGC ( image, "IMAGE %p at [%lx,%lx) registered as %s\n", + image, user_to_phys ( image->data, 0 ), + user_to_phys ( image->data, image->len ), image->name ); + + return 0; +} + +/** + * Unregister executable/loadable 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 ); +} + +/** + * Find image by name + * + * @v name Image name + * @ret image Executable/loadable image, or NULL + */ +struct image * find_image ( const char *name ) { + struct image *image; + + list_for_each_entry ( image, &images, list ) { + if ( strcmp ( image->name, name ) == 0 ) + return image; + } + + return NULL; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @v type Executable/loadable image type + * @ret rc Return status code + */ +static int image_load_type ( struct image *image, struct image_type *type ) { + int rc; + + /* Check image is actually loadable */ + if ( ! type->load ) + return -ENOEXEC; + + /* Try the image loader */ + if ( ( rc = type->load ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not load as %s: %s\n", + image, type->name, strerror ( rc ) ); + return rc; + } + + /* Flag as loaded */ + image->flags |= IMAGE_LOADED; + return 0; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_load ( struct image *image ) { + + assert ( image->type != NULL ); + + return image_load_type ( image, image->type ); +} + +/** + * Autodetect image type and load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_autoload ( struct image *image ) { + struct image_type *type; + int rc; + + /* If image already has a type, use it */ + if ( image->type ) + return image_load ( image ); + + /* Otherwise probe for a suitable type */ + for_each_table_entry ( type, IMAGE_TYPES ) { + DBGC ( image, "IMAGE %p trying type %s\n", image, type->name ); + rc = image_load_type ( image, type ); + if ( image->type == NULL ) + continue; + return rc; + } + + DBGC ( image, "IMAGE %p format not recognised\n", image ); + return -ENOEXEC; +} + +/** + * Execute loaded image + * + * @v image Loaded image + * @ret rc Return status code + */ +int image_exec ( struct image *image ) { + struct image *replacement; + struct uri *old_cwuri; + int rc; + + /* Image must be loaded first */ + if ( ! ( image->flags & IMAGE_LOADED ) ) { + DBGC ( image, "IMAGE %p could not execute: not loaded\n", + image ); + return -ENOTTY; + } + + assert ( image->type != NULL ); + + /* Check that image is actually executable */ + if ( ! image->type->exec ) + return -ENOEXEC; + + /* Switch current working directory to be that of the image itself */ + 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 ) ); + /* Do not return yet; we still have clean-up to do */ + } + + /* Pick up replacement image before we drop the original + * image's temporary reference. + */ + replacement = image->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; +} + +/** + * Register and autoload an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoload_image ( struct image *image ) { + int rc; + + if ( ( rc = register_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_autoload ( image ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Register and autoexec an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoexec_image ( struct image *image ) { + int rc; + + if ( ( rc = register_and_autoload_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_exec ( image ) ) != 0 ) + return rc; + + return 0; +} |