Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / core / image.c
diff --git a/qemu/roms/ipxe/src/core/image.c b/qemu/roms/ipxe/src/core/image.c
new file mode 100644 (file)
index 0000000..ec44802
--- /dev/null
@@ -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;
+}