diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/core/image.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/core/image.c')
-rw-r--r-- | qemu/roms/ipxe/src/core/image.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/core/image.c b/qemu/roms/ipxe/src/core/image.c new file mode 100644 index 000000000..ec4480238 --- /dev/null +++ b/qemu/roms/ipxe/src/core/image.c @@ -0,0 +1,484 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, 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 <syslog.h> +#include <ipxe/list.h> +#include <ipxe/umalloc.h> +#include <ipxe/uri.h> +#include <ipxe/image.h> + +/** @file + * + * Executable images + * + */ + +/* Disambiguate the various error causes */ +#define EACCES_UNTRUSTED \ + __einfo_error ( EINFO_EACCES_UNTRUSTED ) +#define EINFO_EACCES_UNTRUSTED \ + __einfo_uniqify ( EINFO_EACCES, 0x01, "Untrusted image" ) +#define EACCES_PERMANENT \ + __einfo_error ( EINFO_EACCES_PERMANENT ) +#define EINFO_EACCES_PERMANENT \ + __einfo_uniqify ( EINFO_EACCES, 0x02, "Trust requirement is permanent" ) + +/** List of registered images */ +struct list_head images = LIST_HEAD_INIT ( images ); + +/** Currently-executing image */ +struct image *current_image; + +/** Current image trust requirement */ +static int require_trusted_images = 0; + +/** Prevent changes to image trust requirement */ +static int require_trusted_images_permanent = 0; + +/** + * Free executable image + * + * @v refcnt Reference counter + */ +static void free_image ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + DBGC ( image, "IMAGE %s freed\n", image->name ); + free ( image->name ); + free ( image->cmdline ); + uri_put ( image->uri ); + ufree ( image->data ); + image_put ( image->replacement ); + free ( image ); +} + +/** + * Allocate executable image + * + * @v uri URI, or NULL + * @ret image Executable image + */ +struct image * alloc_image ( struct uri *uri ) { + const char *name; + struct image *image; + int rc; + + /* Allocate image */ + image = zalloc ( sizeof ( *image ) ); + if ( ! image ) + goto err_alloc; + + /* Initialise image */ + ref_init ( &image->refcnt, free_image ); + if ( uri ) { + image->uri = uri_get ( uri ); + if ( uri->path ) { + name = basename ( ( char * ) uri->path ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + goto err_set_name; + } + } + + return image; + + err_set_name: + image_put ( image ); + err_alloc: + return NULL; +} + +/** + * Set image name + * + * @v image Image + * @v name New image name + * @ret rc Return status code + */ +int image_set_name ( struct image *image, const char *name ) { + char *name_copy; + + /* Duplicate name */ + name_copy = strdup ( name ); + if ( ! name_copy ) + return -ENOMEM; + + /* Replace existing name */ + free ( image->name ); + image->name = name_copy; + + return 0; +} + +/** + * Set image command line + * + * @v image Image + * @v cmdline New image command line, or NULL + * @ret rc Return status code + */ +int image_set_cmdline ( struct image *image, const char *cmdline ) { + + free ( image->cmdline ); + image->cmdline = NULL; + if ( cmdline ) { + image->cmdline = strdup ( cmdline ); + if ( ! image->cmdline ) + return -ENOMEM; + } + return 0; +} + +/** + * Register executable image + * + * @v image Executable image + * @ret rc Return status code + */ +int register_image ( struct image *image ) { + static unsigned int imgindex = 0; + char name[8]; /* "imgXXXX" */ + int rc; + + /* Create image name if it doesn't already have one */ + if ( ! image->name ) { + snprintf ( name, sizeof ( name ), "img%d", imgindex++ ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + return rc; + } + + /* Avoid ending up with multiple "selected" images on + * re-registration + */ + if ( image_find_selected() ) + image->flags &= ~IMAGE_SELECTED; + + /* Add to image list */ + image_get ( image ); + image->flags |= IMAGE_REGISTERED; + list_add_tail ( &image->list, &images ); + DBGC ( image, "IMAGE %s at [%lx,%lx) registered\n", + image->name, user_to_phys ( image->data, 0 ), + user_to_phys ( image->data, image->len ) ); + + return 0; +} + +/** + * Unregister executable image + * + * @v image Executable image + */ +void unregister_image ( struct image *image ) { + + /* Do nothing unless image is registered */ + if ( ! ( image->flags & IMAGE_REGISTERED ) ) + return; + + DBGC ( image, "IMAGE %s unregistered\n", image->name ); + list_del ( &image->list ); + image->flags &= ~IMAGE_REGISTERED; + image_put ( image ); +} + +/** + * Find image by name + * + * @v name Image name + * @ret image Executable 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; +} + +/** + * Determine image type + * + * @v image Executable image + * @ret rc Return status code + */ +int image_probe ( struct image *image ) { + struct image_type *type; + int rc; + + /* Succeed if we already have a type */ + if ( image->type ) + return 0; + + /* Try each type in turn */ + for_each_table_entry ( type, IMAGE_TYPES ) { + if ( ( rc = type->probe ( image ) ) == 0 ) { + image->type = type; + DBGC ( image, "IMAGE %s is %s\n", + image->name, type->name ); + return 0; + } + DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, + type->name, strerror ( rc ) ); + } + + DBGC ( image, "IMAGE %s format not recognised\n", image->name ); + return -ENOEXEC; +} + +/** + * Execute image + * + * @v image Executable image + * @ret rc Return status code + * + * The image must already be registered. Note that executing an image + * may cause it to unregister itself. The caller must therefore + * assume that the image pointer becomes invalid. + */ +int image_exec ( struct image *image ) { + struct image *saved_current_image; + struct image *replacement = NULL; + struct uri *old_cwuri; + int rc; + + /* Sanity check */ + assert ( image->flags & IMAGE_REGISTERED ); + + /* Switch current working directory to be that of the image itself */ + old_cwuri = uri_get ( cwuri ); + churi ( image->uri ); + + /* Preserve record of any currently-running image */ + saved_current_image = current_image; + + /* Take out a temporary reference to the image. This allows + * the image to unregister itself if necessary, without + * automatically freeing itself. + */ + current_image = image_get ( image ); + + /* Check that this image can be selected for execution */ + if ( ( rc = image_select ( image ) ) != 0 ) + goto err; + + /* Check that image is trusted (if applicable) */ + if ( require_trusted_images && ! ( image->flags & IMAGE_TRUSTED ) ) { + DBGC ( image, "IMAGE %s is not trusted\n", image->name ); + rc = -EACCES_UNTRUSTED; + goto err; + } + + /* Record boot attempt */ + syslog ( LOG_NOTICE, "Executing \"%s\"\n", image->name ); + + /* Try executing the image */ + if ( ( rc = image->type->exec ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not execute: %s\n", + image->name, strerror ( rc ) ); + /* Do not return yet; we still have clean-up to do */ + } + + /* Record result of boot attempt */ + if ( rc == 0 ) { + syslog ( LOG_NOTICE, "Execution of \"%s\" completed\n", + image->name ); + } else { + syslog ( LOG_ERR, "Execution of \"%s\" failed: %s\n", + image->name, strerror ( rc ) ); + } + + /* Pick up replacement image before we drop the original + * image's temporary reference. The replacement image must + * already be registered, so we don't need to hold a temporary + * reference (which would complicate the tail-recursion). + */ + replacement = image->replacement; + if ( replacement ) + assert ( replacement->flags & IMAGE_REGISTERED ); + + err: + /* Unregister image if applicable */ + if ( image->flags & IMAGE_AUTO_UNREGISTER ) + unregister_image ( image ); + + /* Debug message for tail-recursion. Placed here because the + * image_put() may end up freeing the image. + */ + if ( replacement ) { + DBGC ( image, "IMAGE %s replacing self with IMAGE %s\n", + image->name, replacement->name ); + } + + /* Drop temporary reference to the original image */ + image_put ( image ); + + /* Restore previous currently-running image */ + current_image = saved_current_image; + + /* Reset current working directory */ + churi ( old_cwuri ); + uri_put ( old_cwuri ); + + /* Tail-recurse into replacement image, if one exists */ + if ( replacement ) + return image_exec ( replacement ); + + return rc; +} + +/** + * Set replacement image + * + * @v replacement Replacement image + * @ret rc Return status code + * + * The replacement image must already be registered, and must remain + * registered until the currently-executing image returns. + */ +int image_replace ( struct image *replacement ) { + struct image *image = current_image; + int rc; + + /* Sanity check */ + assert ( replacement->flags & IMAGE_REGISTERED ); + + /* Fail unless there is a currently-executing image */ + if ( ! image ) { + rc = -ENOTTY; + DBGC ( replacement, "IMAGE %s cannot replace non-existent " + "image: %s\n", replacement->name, strerror ( rc ) ); + return rc; + } + + /* Check that the replacement image can be executed */ + if ( ( rc = image_probe ( replacement ) ) != 0 ) + return rc; + + /* Clear any existing replacement */ + image_put ( image->replacement ); + + /* Set replacement */ + image->replacement = image_get ( replacement ); + DBGC ( image, "IMAGE %s will replace self with IMAGE %s\n", + image->name, replacement->name ); + + return 0; +} + +/** + * Select image for execution + * + * @v image Executable image + * @ret rc Return status code + */ +int image_select ( struct image *image ) { + struct image *tmp; + int rc; + + /* Unselect all other images */ + for_each_image ( tmp ) + tmp->flags &= ~IMAGE_SELECTED; + + /* Check that this image can be executed */ + if ( ( rc = image_probe ( image ) ) != 0 ) + return rc; + if ( ! image->type->exec ) + return -ENOEXEC; + + /* Mark image as selected */ + image->flags |= IMAGE_SELECTED; + + return 0; +} + +/** + * Find selected image + * + * @ret image Executable image, or NULL + */ +struct image * image_find_selected ( void ) { + struct image *image; + + for_each_image ( image ) { + if ( image->flags & IMAGE_SELECTED ) + return image; + } + return NULL; +} + +/** + * Change image trust requirement + * + * @v require_trusted Require trusted images + * @v permanent Make trust requirement permanent + * @ret rc Return status code + */ +int image_set_trust ( int require_trusted, int permanent ) { + + /* Update trust requirement, if permitted to do so */ + if ( ! require_trusted_images_permanent ) { + require_trusted_images = require_trusted; + require_trusted_images_permanent = permanent; + } + + /* Fail if we attempted to change the trust requirement but + * were not permitted to do so. + */ + if ( require_trusted_images != require_trusted ) + return -EACCES_PERMANENT; + + return 0; +} + +/** + * Create pixel buffer from image + * + * @v image Image + * @v pixbuf Pixel buffer to fill in + * @ret rc Return status code + */ +int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { + int rc; + + /* Check that this image can be used to create a pixel buffer */ + if ( ( rc = image_probe ( image ) ) != 0 ) + return rc; + if ( ! image->type->pixbuf ) + return -ENOTSUP; + + /* Try creating pixel buffer */ + if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} |