/* * 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; }