summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/image
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/image')
-rw-r--r--qemu/roms/ipxe/src/image/efi_image.c316
-rw-r--r--qemu/roms/ipxe/src/image/elf.c185
-rw-r--r--qemu/roms/ipxe/src/image/embedded.c91
-rw-r--r--qemu/roms/ipxe/src/image/png.c1007
-rw-r--r--qemu/roms/ipxe/src/image/pnm.c415
-rw-r--r--qemu/roms/ipxe/src/image/script.c423
-rw-r--r--qemu/roms/ipxe/src/image/segment.c91
7 files changed, 2528 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/image/efi_image.c b/qemu/roms/ipxe/src/image/efi_image.c
new file mode 100644
index 000000000..b7d8f9c6e
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/efi_image.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2008 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 <errno.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_snp.h>
+#include <ipxe/efi/efi_download.h>
+#include <ipxe/efi/efi_file.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_wrap.h>
+#include <ipxe/image.h>
+#include <ipxe/init.h>
+#include <ipxe/features.h>
+#include <ipxe/uri.h>
+
+FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
+
+/* Disambiguate the various error causes */
+#define EINFO_EEFI_LOAD \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "Could not load image" )
+#define EINFO_EEFI_LOAD_PROHIBITED \
+ __einfo_platformify ( EINFO_EEFI_LOAD, EFI_SECURITY_VIOLATION, \
+ "Image prohibited by security policy" )
+#define EEFI_LOAD_PROHIBITED \
+ __einfo_error ( EINFO_EEFI_LOAD_PROHIBITED )
+#define EEFI_LOAD( efirc ) EPLATFORM ( EINFO_EEFI_LOAD, efirc, \
+ EEFI_LOAD_PROHIBITED )
+#define EINFO_EEFI_START \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x02, \
+ "Could not start image" )
+#define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc )
+
+/**
+ * Create device path for image
+ *
+ * @v image EFI image
+ * @v parent Parent device path
+ * @ret path Device path, or NULL on failure
+ *
+ * The caller must eventually free() the device path.
+ */
+static EFI_DEVICE_PATH_PROTOCOL *
+efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ FILEPATH_DEVICE_PATH *filepath;
+ EFI_DEVICE_PATH_PROTOCOL *end;
+ size_t name_len;
+ size_t prefix_len;
+ size_t filepath_len;
+ size_t len;
+
+ /* Calculate device path lengths */
+ end = efi_devpath_end ( parent );
+ prefix_len = ( ( void * ) end - ( void * ) parent );
+ name_len = strlen ( image->name );
+ filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH +
+ ( name_len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
+ len = ( prefix_len + filepath_len + sizeof ( *end ) );
+
+ /* Allocate device path */
+ path = zalloc ( len );
+ if ( ! path )
+ return NULL;
+
+ /* Construct device path */
+ memcpy ( path, parent, prefix_len );
+ filepath = ( ( ( void * ) path ) + prefix_len );
+ filepath->Header.Type = MEDIA_DEVICE_PATH;
+ filepath->Header.SubType = MEDIA_FILEPATH_DP;
+ filepath->Header.Length[0] = ( filepath_len & 0xff );
+ filepath->Header.Length[1] = ( filepath_len >> 8 );
+ efi_snprintf ( filepath->PathName, ( name_len + 1 /* NUL */ ),
+ "%s", image->name );
+ end = ( ( ( void * ) filepath ) + filepath_len );
+ end->Type = END_DEVICE_PATH_TYPE;
+ end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ end->Length[0] = sizeof ( *end );
+
+ return path;
+}
+
+/**
+ * Create command line for image
+ *
+ * @v image EFI image
+ * @ret cmdline Command line, or NULL on failure
+ */
+static wchar_t * efi_image_cmdline ( struct image *image ) {
+ wchar_t *cmdline;
+ size_t len;
+
+ len = ( strlen ( image->name ) +
+ ( image->cmdline ?
+ ( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
+ cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
+ if ( ! cmdline )
+ return NULL;
+ efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
+ image->name,
+ ( image->cmdline ? " " : "" ),
+ ( image->cmdline ? image->cmdline : "" ) );
+ return cmdline;
+}
+
+/**
+ * Execute EFI image
+ *
+ * @v image EFI image
+ * @ret rc Return status code
+ */
+static int efi_image_exec ( struct image *image ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_snp_device *snpdev;
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ union {
+ EFI_LOADED_IMAGE_PROTOCOL *image;
+ void *interface;
+ } loaded;
+ EFI_HANDLE handle;
+ wchar_t *cmdline;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Find an appropriate device handle to use */
+ snpdev = last_opened_snpdev();
+ if ( ! snpdev ) {
+ DBGC ( image, "EFIIMAGE %p could not identify SNP device\n",
+ image );
+ rc = -ENODEV;
+ goto err_no_snpdev;
+ }
+
+ /* Install file I/O protocols */
+ if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) {
+ DBGC ( image, "EFIIMAGE %p could not install file protocol: "
+ "%s\n", image, strerror ( rc ) );
+ goto err_file_install;
+ }
+
+ /* Install iPXE download protocol */
+ if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) {
+ DBGC ( image, "EFIIMAGE %p could not install iPXE download "
+ "protocol: %s\n", image, strerror ( rc ) );
+ goto err_download_install;
+ }
+
+ /* Create device path for image */
+ path = efi_image_path ( image, snpdev->path );
+ if ( ! path ) {
+ DBGC ( image, "EFIIMAGE %p could not create device path\n",
+ image );
+ rc = -ENOMEM;
+ goto err_image_path;
+ }
+
+ /* Create command line for image */
+ cmdline = efi_image_cmdline ( image );
+ if ( ! cmdline ) {
+ DBGC ( image, "EFIIMAGE %p could not create command line\n",
+ image );
+ rc = -ENOMEM;
+ goto err_cmdline;
+ }
+
+ /* Attempt loading image */
+ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path,
+ user_to_virt ( image->data, 0 ),
+ image->len, &handle ) ) != 0 ) {
+ /* Not an EFI image */
+ rc = -EEFI_LOAD ( efirc );
+ DBGC ( image, "EFIIMAGE %p could not load: %s\n",
+ image, strerror ( rc ) );
+ goto err_load_image;
+ }
+
+ /* Get the loaded image protocol for the newly loaded image */
+ efirc = bs->OpenProtocol ( handle, &efi_loaded_image_protocol_guid,
+ &loaded.interface, efi_image_handle,
+ NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL );
+ if ( efirc ) {
+ /* Should never happen */
+ rc = -EEFI ( efirc );
+ goto err_open_protocol;
+ }
+
+ /* Some EFI 1.10 implementations seem not to fill in DeviceHandle */
+ if ( loaded.image->DeviceHandle == NULL ) {
+ DBGC ( image, "EFIIMAGE %p filling in missing DeviceHandle\n",
+ image );
+ loaded.image->DeviceHandle = snpdev->handle;
+ }
+
+ /* Sanity checks */
+ assert ( loaded.image->ParentHandle == efi_image_handle );
+ assert ( loaded.image->DeviceHandle == snpdev->handle );
+ assert ( loaded.image->LoadOptionsSize == 0 );
+ assert ( loaded.image->LoadOptions == NULL );
+
+ /* Set command line */
+ loaded.image->LoadOptions = cmdline;
+ loaded.image->LoadOptionsSize =
+ ( ( wcslen ( cmdline ) + 1 /* NUL */ ) * sizeof ( wchar_t ) );
+
+ /* Release network devices for use via SNP */
+ efi_snp_release();
+
+ /* Wrap calls made by the loaded image (for debugging) */
+ efi_wrap ( handle );
+
+ /* Start the image */
+ if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
+ rc = -EEFI_START ( efirc );
+ DBGC ( image, "EFIIMAGE %p could not start (or returned with "
+ "error): %s\n", image, strerror ( rc ) );
+ goto err_start_image;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_start_image:
+ efi_snp_claim();
+ err_open_protocol:
+ /* If there was no error, then the image must have been
+ * started and returned successfully. It either unloaded
+ * itself, or it intended to remain loaded (e.g. it was a
+ * driver). We therefore do not unload successful images.
+ *
+ * If there was an error, attempt to unload the image. This
+ * may not work. In particular, there is no way to tell
+ * whether an error returned from StartImage() was due to
+ * being unable to start the image (in which case we probably
+ * should call UnloadImage()), or due to the image itself
+ * returning an error (in which case we probably should not
+ * call UnloadImage()). We therefore ignore any failures from
+ * the UnloadImage() call itself.
+ */
+ if ( rc != 0 )
+ bs->UnloadImage ( handle );
+ err_load_image:
+ free ( cmdline );
+ err_cmdline:
+ free ( path );
+ err_image_path:
+ efi_download_uninstall ( snpdev->handle );
+ err_download_install:
+ efi_file_uninstall ( snpdev->handle );
+ err_file_install:
+ err_no_snpdev:
+ return rc;
+}
+
+/**
+ * Probe EFI image
+ *
+ * @v image EFI file
+ * @ret rc Return status code
+ */
+static int efi_image_probe ( struct image *image ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ static EFI_DEVICE_PATH_PROTOCOL empty_path = {
+ .Type = END_DEVICE_PATH_TYPE,
+ .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ .Length[0] = sizeof ( empty_path ),
+ };
+ EFI_HANDLE handle;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Attempt loading image */
+ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, &empty_path,
+ user_to_virt ( image->data, 0 ),
+ image->len, &handle ) ) != 0 ) {
+ /* Not an EFI image */
+ rc = -EEFI_LOAD ( efirc );
+ DBGC ( image, "EFIIMAGE %p could not load: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Unload the image. We can't leave it loaded, because we
+ * have no "unload" operation.
+ */
+ bs->UnloadImage ( handle );
+
+ return 0;
+}
+
+/** EFI image type */
+struct image_type efi_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "EFI",
+ .probe = efi_image_probe,
+ .exec = efi_image_exec,
+};
diff --git a/qemu/roms/ipxe/src/image/elf.c b/qemu/roms/ipxe/src/image/elf.c
new file mode 100644
index 000000000..51636a8e9
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/elf.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007 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 );
+
+/**
+ * @file
+ *
+ * ELF image format
+ *
+ * A "pure" ELF image is not a bootable image. There are various
+ * bootable formats based upon ELF (e.g. Multiboot), which share
+ * common ELF-related functionality.
+ */
+
+#include <errno.h>
+#include <elf.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/segment.h>
+#include <ipxe/image.h>
+#include <ipxe/elf.h>
+
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Phdr Elf_Phdr;
+typedef Elf32_Off Elf_Off;
+#define ELFCLASS ELFCLASS32
+
+/**
+ * Load ELF segment into memory
+ *
+ * @v image ELF file
+ * @v phdr ELF program header
+ * @v ehdr ELF executable header
+ * @ret entry Entry point, if found
+ * @ret max Maximum used address
+ * @ret rc Return status code
+ */
+static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
+ Elf_Ehdr *ehdr, physaddr_t *entry,
+ physaddr_t *max ) {
+ physaddr_t dest;
+ physaddr_t end;
+ userptr_t buffer;
+ unsigned long e_offset;
+ int rc;
+
+ /* Do nothing for non-PT_LOAD segments */
+ if ( phdr->p_type != PT_LOAD )
+ return 0;
+
+ /* Check segment lies within image */
+ if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
+ DBGC ( image, "ELF %p segment outside image\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Find start address: use physical address for preference,
+ * fall back to virtual address if no physical address
+ * supplied.
+ */
+ dest = phdr->p_paddr;
+ if ( ! dest )
+ dest = phdr->p_vaddr;
+ if ( ! dest ) {
+ DBGC ( image, "ELF %p segment loads to physical address 0\n",
+ image );
+ return -ENOEXEC;
+ }
+ buffer = phys_to_user ( dest );
+ end = ( dest + phdr->p_memsz );
+
+ DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
+ phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
+ phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
+ ( phdr->p_paddr + phdr->p_memsz ) );
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
+ phdr->p_memsz ) ) != 0 ) {
+ DBGC ( image, "ELF %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Update maximum used address, if applicable */
+ if ( end > *max )
+ *max = end;
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
+
+ /* Set execution address, if it lies within this segment */
+ if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
+ *entry = ehdr->e_entry;
+ DBGC ( image, "ELF %p found physical entry point at %lx\n",
+ image, *entry );
+ } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
+ < phdr->p_filesz ) {
+ if ( ! *entry ) {
+ *entry = ( dest + e_offset );
+ DBGC ( image, "ELF %p found virtual entry point at %lx"
+ " (virt %lx)\n", image, *entry,
+ ( ( unsigned long ) ehdr->e_entry ) );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Load ELF image into memory
+ *
+ * @v image ELF file
+ * @ret entry Entry point
+ * @ret max Maximum used address
+ * @ret rc Return status code
+ */
+int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
+ static const uint8_t e_ident[] = {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELFCLASS,
+ };
+ Elf_Ehdr ehdr;
+ Elf_Phdr phdr;
+ Elf_Off phoff;
+ unsigned int phnum;
+ int rc;
+
+ /* Read ELF header */
+ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
+ if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident,
+ sizeof ( e_ident ) ) != 0 ) {
+ DBGC ( image, "ELF %p has invalid signature\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Initialise maximum used address */
+ *max = 0;
+
+ /* Invalidate entry point */
+ *entry = 0;
+
+ /* Read ELF program headers */
+ for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ;
+ phoff += ehdr.e_phentsize, phnum-- ) {
+ if ( phoff > image->len ) {
+ DBGC ( image, "ELF %p program header %d outside "
+ "image\n", image, phnum );
+ return -ENOEXEC;
+ }
+ copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
+ if ( ( rc = elf_load_segment ( image, &phdr, &ehdr,
+ entry, max ) ) != 0 ) {
+ return rc;
+ }
+ }
+
+ /* Check for a valid execution address */
+ if ( ! *entry ) {
+ DBGC ( image, "ELF %p entry point %lx outside image\n",
+ image, ( ( unsigned long ) ehdr.e_entry ) );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
diff --git a/qemu/roms/ipxe/src/image/embedded.c b/qemu/roms/ipxe/src/image/embedded.c
new file mode 100644
index 000000000..6358378fb
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/embedded.c
@@ -0,0 +1,91 @@
+/** @file
+ *
+ * Embedded image support
+ *
+ * Embedded images are images built into the iPXE binary and do not require
+ * fetching over the network.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/init.h>
+
+/* Raw image data for all embedded images */
+#undef EMBED
+#define EMBED( _index, _path, _name ) \
+ extern char embedded_image_ ## _index ## _data[]; \
+ extern char embedded_image_ ## _index ## _len[]; \
+ __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \
+ "\nembedded_image_" #_index "_data:\n\t" \
+ ".incbin \"" _path "\"\n\t" \
+ "\nembedded_image_" #_index "_end:\n\t" \
+ ".equ embedded_image_" #_index "_len, " \
+ "( embedded_image_" #_index "_end - " \
+ " embedded_image_" #_index "_data )\n\t" \
+ ".previous\n\t" );
+EMBED_ALL
+
+/* Image structures for all embedded images */
+#undef EMBED
+#define EMBED( _index, _path, _name ) { \
+ .refcnt = REF_INIT ( ref_no_free ), \
+ .name = _name, \
+ .data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \
+ .len = ( size_t ) embedded_image_ ## _index ## _len, \
+},
+static struct image embedded_images[] = {
+ EMBED_ALL
+};
+
+/**
+ * Register all embedded images
+ */
+static void embedded_init ( void ) {
+ int i;
+ struct image *image;
+ void *data;
+ int rc;
+
+ /* Skip if we have no embedded images */
+ if ( ! sizeof ( embedded_images ) )
+ return;
+
+ /* Fix up data pointers and register images */
+ for ( i = 0 ; i < ( int ) ( sizeof ( embedded_images ) /
+ sizeof ( embedded_images[0] ) ) ; i++ ) {
+ image = &embedded_images[i];
+
+ /* virt_to_user() cannot be used in a static
+ * initialiser, so we cast the pointer to a userptr_t
+ * in the initialiser and fix it up here. (This will
+ * actually be a no-op on most platforms.)
+ */
+ data = ( ( void * ) image->data );
+ image->data = virt_to_user ( data );
+
+ DBG ( "Embedded image \"%s\": %zd bytes at %p\n",
+ image->name, image->len, data );
+
+ if ( ( rc = register_image ( image ) ) != 0 ) {
+ DBG ( "Could not register embedded image \"%s\": "
+ "%s\n", image->name, strerror ( rc ) );
+ return;
+ }
+ }
+
+ /* Select the first image */
+ image = &embedded_images[0];
+ if ( ( rc = image_select ( image ) ) != 0 ) {
+ DBG ( "Could not select embedded image \"%s\": %s\n",
+ image->name, strerror ( rc ) );
+ return;
+ }
+}
+
+/** Embedded image initialisation function */
+struct init_fn embedded_init_fn __init_fn ( INIT_LATE ) = {
+ .initialise = embedded_init,
+};
diff --git a/qemu/roms/ipxe/src/image/png.c b/qemu/roms/ipxe/src/image/png.c
new file mode 100644
index 000000000..c14608553
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/png.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2014 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/umalloc.h>
+#include <ipxe/pixbuf.h>
+#include <ipxe/deflate.h>
+#include <ipxe/png.h>
+
+/** @file
+ *
+ * Portable Network Graphics (PNG) format
+ *
+ * The PNG format is defined in RFC 2083.
+ */
+
+/** PNG context */
+struct png_context {
+ /** Offset within image */
+ size_t offset;
+
+ /** Pixel buffer */
+ struct pixel_buffer *pixbuf;
+
+ /** Bit depth */
+ unsigned int depth;
+ /** Colour type */
+ unsigned int colour_type;
+ /** Number of channels */
+ unsigned int channels;
+ /** Number of interlace passes */
+ unsigned int passes;
+ /** Palette, in iPXE's pixel buffer format */
+ uint32_t palette[PNG_PALETTE_COUNT];
+
+ /** Decompression buffer for raw PNG data */
+ struct deflate_chunk raw;
+ /** Decompressor */
+ struct deflate deflate;
+};
+
+/** A PNG interlace pass */
+struct png_interlace {
+ /** Pass number */
+ unsigned int pass;
+ /** X starting indent */
+ unsigned int x_indent;
+ /** Y starting indent */
+ unsigned int y_indent;
+ /** X stride */
+ unsigned int x_stride;
+ /** Y stride */
+ unsigned int y_stride;
+ /** Width */
+ unsigned int width;
+ /** Height */
+ unsigned int height;
+};
+
+/** PNG file signature */
+static struct png_signature png_signature = PNG_SIGNATURE;
+
+/** Number of interlacing passes */
+static uint8_t png_interlace_passes[] = {
+ [PNG_INTERLACE_NONE] = 1,
+ [PNG_INTERLACE_ADAM7] = 7,
+};
+
+/**
+ * Transcribe PNG chunk type name (for debugging)
+ *
+ * @v type Chunk type
+ * @ret name Chunk type name
+ */
+static const char * png_type_name ( uint32_t type ) {
+ static union {
+ uint32_t type;
+ char name[ sizeof ( uint32_t ) + 1 /* NUL */ ];
+ } u;
+
+ u.type = type;
+ return u.name;
+}
+
+/**
+ * Calculate PNG interlace pass parameters
+ *
+ * @v png PNG context
+ * @v pass Pass number (0=first pass)
+ * @v interlace Interlace pass to fill in
+ */
+static void png_interlace ( struct png_context *png, unsigned int pass,
+ struct png_interlace *interlace ) {
+ unsigned int grid_width_log2;
+ unsigned int grid_height_log2;
+ unsigned int x_indent;
+ unsigned int y_indent;
+ unsigned int x_stride_log2;
+ unsigned int y_stride_log2;
+ unsigned int x_stride;
+ unsigned int y_stride;
+ unsigned int width;
+ unsigned int height;
+
+ /* Sanity check */
+ assert ( png->passes > 0 );
+
+ /* Store pass number */
+ interlace->pass = pass;
+
+ /* Calculate interlace grid dimensions */
+ grid_width_log2 = ( png->passes / 2 );
+ grid_height_log2 = ( ( png->passes - 1 ) / 2 );
+
+ /* Calculate starting indents */
+ interlace->x_indent = x_indent =
+ ( ( pass & 1 ) ?
+ ( 1 << ( grid_width_log2 - ( pass / 2 ) - 1 ) ) : 0 );
+ interlace->y_indent = y_indent =
+ ( ( pass && ! ( pass & 1 ) ) ?
+ ( 1 << ( grid_height_log2 - ( ( pass - 1 ) / 2 ) - 1 ) ) : 0);
+
+ /* Calculate strides */
+ x_stride_log2 = ( grid_width_log2 - ( pass / 2 ) );
+ y_stride_log2 =
+ ( grid_height_log2 - ( pass ? ( ( pass - 1 ) / 2 ) : 0 ) );
+ interlace->x_stride = x_stride = ( 1 << x_stride_log2 );
+ interlace->y_stride = y_stride = ( 1 << y_stride_log2 );
+
+ /* Calculate pass dimensions */
+ width = png->pixbuf->width;
+ height = png->pixbuf->height;
+ interlace->width =
+ ( ( width - x_indent + x_stride - 1 ) >> x_stride_log2 );
+ interlace->height =
+ ( ( height - y_indent + y_stride - 1 ) >> y_stride_log2 );
+}
+
+/**
+ * Calculate PNG pixel length
+ *
+ * @v png PNG context
+ * @ret pixel_len Pixel length
+ */
+static unsigned int png_pixel_len ( struct png_context *png ) {
+
+ return ( ( ( png->channels * png->depth ) + 7 ) / 8 );
+}
+
+/**
+ * Calculate PNG scanline length
+ *
+ * @v png PNG context
+ * @v interlace Interlace pass
+ * @ret scanline_len Scanline length (including filter byte)
+ */
+static size_t png_scanline_len ( struct png_context *png,
+ struct png_interlace *interlace ) {
+
+ return ( 1 /* Filter byte */ +
+ ( ( interlace->width * png->channels * png->depth ) + 7 ) / 8);
+}
+
+/**
+ * Handle PNG image header chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+static int png_image_header ( struct image *image, struct png_context *png,
+ size_t len ) {
+ struct png_image_header ihdr;
+ struct png_interlace interlace;
+ unsigned int pass;
+
+ /* Sanity check */
+ if ( len != sizeof ( ihdr ) ) {
+ DBGC ( image, "PNG %s invalid IHDR length %zd\n",
+ image->name, len );
+ return -EINVAL;
+ }
+ if ( png->pixbuf ) {
+ DBGC ( image, "PNG %s duplicate IHDR\n", image->name );
+ return -EINVAL;
+ }
+
+ /* Extract image header */
+ copy_from_user ( &ihdr, image->data, png->offset, len );
+ DBGC ( image, "PNG %s %dx%d depth %d type %d compression %d filter %d "
+ "interlace %d\n", image->name, ntohl ( ihdr.width ),
+ ntohl ( ihdr.height ), ihdr.depth, ihdr.colour_type,
+ ihdr.compression, ihdr.filter, ihdr.interlace );
+
+ /* Sanity checks */
+ if ( ihdr.compression >= PNG_COMPRESSION_UNKNOWN ) {
+ DBGC ( image, "PNG %s unknown compression method %d\n",
+ image->name, ihdr.compression );
+ return -ENOTSUP;
+ }
+ if ( ihdr.filter >= PNG_FILTER_UNKNOWN ) {
+ DBGC ( image, "PNG %s unknown filter method %d\n",
+ image->name, ihdr.filter );
+ return -ENOTSUP;
+ }
+ if ( ihdr.interlace >= PNG_INTERLACE_UNKNOWN ) {
+ DBGC ( image, "PNG %s unknown interlace method %d\n",
+ image->name, ihdr.interlace );
+ return -ENOTSUP;
+ }
+
+ /* Allocate pixel buffer */
+ png->pixbuf = alloc_pixbuf ( ntohl ( ihdr.width ),
+ ntohl ( ihdr.height ) );
+ if ( ! png->pixbuf ) {
+ DBGC ( image, "PNG %s could not allocate pixel buffer\n",
+ image->name );
+ return -ENOMEM;
+ }
+
+ /* Extract bit depth */
+ png->depth = ihdr.depth;
+ if ( ( png->depth == 0 ) ||
+ ( ( png->depth & ( png->depth - 1 ) ) != 0 ) ) {
+ DBGC ( image, "PNG %s invalid depth %d\n",
+ image->name, png->depth );
+ return -EINVAL;
+ }
+
+ /* Calculate number of channels */
+ png->colour_type = ihdr.colour_type;
+ png->channels = 1;
+ if ( ! ( ihdr.colour_type & PNG_COLOUR_TYPE_PALETTE ) ) {
+ if ( ihdr.colour_type & PNG_COLOUR_TYPE_RGB )
+ png->channels += 2;
+ if ( ihdr.colour_type & PNG_COLOUR_TYPE_ALPHA )
+ png->channels += 1;
+ }
+
+ /* Calculate number of interlace passes */
+ png->passes = png_interlace_passes[ihdr.interlace];
+
+ /* Calculate length of raw data buffer */
+ for ( pass = 0 ; pass < png->passes ; pass++ ) {
+ png_interlace ( png, pass, &interlace );
+ if ( interlace.width == 0 )
+ continue;
+ png->raw.len += ( interlace.height *
+ png_scanline_len ( png, &interlace ) );
+ }
+
+ /* Allocate raw data buffer */
+ png->raw.data = umalloc ( png->raw.len );
+ if ( ! png->raw.data ) {
+ DBGC ( image, "PNG %s could not allocate data buffer\n",
+ image->name );
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle PNG palette chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+static int png_palette ( struct image *image, struct png_context *png,
+ size_t len ) {
+ size_t offset = png->offset;
+ struct png_palette_entry palette;
+ unsigned int i;
+
+ /* Populate palette */
+ for ( i = 0 ; i < ( sizeof ( png->palette ) /
+ sizeof ( png->palette[0] ) ) ; i++ ) {
+
+ /* Stop when we run out of palette data */
+ if ( len < sizeof ( palette ) )
+ break;
+
+ /* Extract palette entry */
+ copy_from_user ( &palette, image->data, offset,
+ sizeof ( palette ) );
+ png->palette[i] = ( ( palette.red << 16 ) |
+ ( palette.green << 8 ) |
+ ( palette.blue << 0 ) );
+ DBGC2 ( image, "PNG %s palette entry %d is %#06x\n",
+ image->name, i, png->palette[i] );
+
+ /* Move to next entry */
+ offset += sizeof ( palette );
+ len -= sizeof ( palette );
+ }
+
+ return 0;
+}
+
+/**
+ * Handle PNG image data chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+static int png_image_data ( struct image *image, struct png_context *png,
+ size_t len ) {
+ struct deflate_chunk in;
+ int rc;
+
+ /* Deflate this chunk */
+ deflate_chunk_init ( &in, image->data, png->offset,
+ ( png->offset + len ) );
+ if ( ( rc = deflate_inflate ( &png->deflate, &in, &png->raw ) ) != 0 ) {
+ DBGC ( image, "PNG %s could not decompress: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Unfilter byte using the "None" filter
+ *
+ * @v current Filtered current byte
+ * @v left Unfiltered left byte
+ * @v above Unfiltered above byte
+ * @v above_left Unfiltered above-left byte
+ * @ret current Unfiltered current byte
+ */
+static unsigned int png_unfilter_none ( unsigned int current,
+ unsigned int left __unused,
+ unsigned int above __unused,
+ unsigned int above_left __unused ) {
+
+ return current;
+}
+
+/**
+ * Unfilter byte using the "Sub" filter
+ *
+ * @v current Filtered current byte
+ * @v left Unfiltered left byte
+ * @v above Unfiltered above byte
+ * @v above_left Unfiltered above-left byte
+ * @ret current Unfiltered current byte
+ */
+static unsigned int png_unfilter_sub ( unsigned int current,
+ unsigned int left,
+ unsigned int above __unused,
+ unsigned int above_left __unused ) {
+
+ return ( current + left );
+}
+
+/**
+ * Unfilter byte using the "Up" filter
+ *
+ * @v current Filtered current byte
+ * @v left Unfiltered left byte
+ * @v above Unfiltered above byte
+ * @v above_left Unfiltered above-left byte
+ * @ret current Unfiltered current byte
+ */
+static unsigned int png_unfilter_up ( unsigned int current,
+ unsigned int left __unused,
+ unsigned int above,
+ unsigned int above_left __unused ) {
+
+ return ( current + above );
+}
+
+/**
+ * Unfilter byte using the "Average" filter
+ *
+ * @v current Filtered current byte
+ * @v left Unfiltered left byte
+ * @v above Unfiltered above byte
+ * @v above_left Unfiltered above-left byte
+ * @ret current Unfiltered current byte
+ */
+static unsigned int png_unfilter_average ( unsigned int current,
+ unsigned int left,
+ unsigned int above,
+ unsigned int above_left __unused ) {
+
+ return ( current + ( ( above + left ) >> 1 ) );
+}
+
+/**
+ * Paeth predictor function (defined in RFC 2083)
+ *
+ * @v a Pixel A
+ * @v b Pixel B
+ * @v c Pixel C
+ * @ret predictor Predictor pixel
+ */
+static unsigned int png_paeth_predictor ( unsigned int a, unsigned int b,
+ unsigned int c ) {
+ unsigned int p;
+ unsigned int pa;
+ unsigned int pb;
+ unsigned int pc;
+
+ /* Algorithm as defined in RFC 2083 section 6.6 */
+ p = ( a + b - c );
+ pa = abs ( p - a );
+ pb = abs ( p - b );
+ pc = abs ( p - c );
+ if ( ( pa <= pb ) && ( pa <= pc ) ) {
+ return a;
+ } else if ( pb <= pc ) {
+ return b;
+ } else {
+ return c;
+ }
+}
+
+/**
+ * Unfilter byte using the "Paeth" filter
+ *
+ * @v current Filtered current byte
+ * @v above_left Unfiltered above-left byte
+ * @v above Unfiltered above byte
+ * @v left Unfiltered left byte
+ * @ret current Unfiltered current byte
+ */
+static unsigned int png_unfilter_paeth ( unsigned int current,
+ unsigned int left,
+ unsigned int above,
+ unsigned int above_left ) {
+
+ return ( current + png_paeth_predictor ( left, above, above_left ) );
+}
+
+/** A PNG filter */
+struct png_filter {
+ /**
+ * Unfilter byte
+ *
+ * @v current Filtered current byte
+ * @v left Unfiltered left byte
+ * @v above Unfiltered above byte
+ * @v above_left Unfiltered above-left byte
+ * @ret current Unfiltered current byte
+ */
+ unsigned int ( * unfilter ) ( unsigned int current,
+ unsigned int left,
+ unsigned int above,
+ unsigned int above_left );
+};
+
+/** PNG filter types */
+static struct png_filter png_filters[] = {
+ [PNG_FILTER_BASIC_NONE] = { png_unfilter_none },
+ [PNG_FILTER_BASIC_SUB] = { png_unfilter_sub },
+ [PNG_FILTER_BASIC_UP] = { png_unfilter_up },
+ [PNG_FILTER_BASIC_AVERAGE] = { png_unfilter_average },
+ [PNG_FILTER_BASIC_PAETH] = { png_unfilter_paeth },
+};
+
+/**
+ * Unfilter one interlace pass of PNG raw data
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v interlace Interlace pass
+ * @ret rc Return status code
+ *
+ * This routine may assume that it is impossible to overrun the raw
+ * data buffer, since the size is determined by the image dimensions.
+ */
+static int png_unfilter_pass ( struct image *image, struct png_context *png,
+ struct png_interlace *interlace ) {
+ size_t offset = png->raw.offset;
+ size_t pixel_len = png_pixel_len ( png );
+ size_t scanline_len = png_scanline_len ( png, interlace );
+ struct png_filter *filter;
+ unsigned int scanline;
+ unsigned int byte;
+ uint8_t filter_type;
+ uint8_t left;
+ uint8_t above;
+ uint8_t above_left;
+ uint8_t current;
+
+ /* On the first scanline of a pass, above bytes are assumed to
+ * be zero.
+ */
+ above = 0;
+
+ /* Iterate over each scanline in turn */
+ for ( scanline = 0 ; scanline < interlace->height ; scanline++ ) {
+
+ /* Extract filter byte and determine filter type */
+ copy_from_user ( &filter_type, png->raw.data, offset++,
+ sizeof ( filter_type ) );
+ if ( filter_type >= ( sizeof ( png_filters ) /
+ sizeof ( png_filters[0] ) ) ) {
+ DBGC ( image, "PNG %s unknown filter type %d\n",
+ image->name, filter_type );
+ return -ENOTSUP;
+ }
+ filter = &png_filters[filter_type];
+ assert ( filter->unfilter != NULL );
+ DBGC2 ( image, "PNG %s pass %d scanline %d filter type %d\n",
+ image->name, interlace->pass, scanline, filter_type );
+
+ /* At the start of a line, both above-left and left
+ * bytes are taken to be zero.
+ */
+ left = 0;
+ above_left = 0;
+
+ /* Iterate over each byte (not pixel) in turn */
+ for ( byte = 0 ; byte < ( scanline_len - 1 ) ; byte++ ) {
+
+ /* Extract predictor bytes, if applicable */
+ if ( byte >= pixel_len ) {
+ copy_from_user ( &left, png->raw.data,
+ ( offset - pixel_len ),
+ sizeof ( left ) );
+ }
+ if ( scanline > 0 ) {
+ copy_from_user ( &above, png->raw.data,
+ ( offset - scanline_len ),
+ sizeof ( above ) );
+ }
+ if ( ( scanline > 0 ) && ( byte >= pixel_len ) ) {
+ copy_from_user ( &above_left, png->raw.data,
+ ( offset - scanline_len -
+ pixel_len ),
+ sizeof ( above_left ) );
+ }
+
+ /* Unfilter current byte */
+ copy_from_user ( &current, png->raw.data,
+ offset, sizeof ( current ) );
+ current = filter->unfilter ( current, left, above,
+ above_left );
+ copy_to_user ( png->raw.data, offset++,
+ &current, sizeof ( current ) );
+ }
+ }
+
+ /* Update offset */
+ png->raw.offset = offset;
+
+ return 0;
+}
+
+/**
+ * Unfilter PNG raw data
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @ret rc Return status code
+ *
+ * This routine may assume that it is impossible to overrun the raw
+ * data buffer, since the size is determined by the image dimensions.
+ */
+static int png_unfilter ( struct image *image, struct png_context *png ) {
+ struct png_interlace interlace;
+ unsigned int pass;
+ int rc;
+
+ /* Process each interlace pass */
+ png->raw.offset = 0;
+ for ( pass = 0 ; pass < png->passes ; pass++ ) {
+
+ /* Calculate interlace pass parameters */
+ png_interlace ( png, pass, &interlace );
+
+ /* Skip zero-width rows (which have no filter bytes) */
+ if ( interlace.width == 0 )
+ continue;
+
+ /* Unfilter this pass */
+ if ( ( rc = png_unfilter_pass ( image, png,
+ &interlace ) ) != 0 )
+ return rc;
+ }
+ assert ( png->raw.offset == png->raw.len );
+
+ return 0;
+}
+
+/**
+ * Calculate PNG pixel component value
+ *
+ * @v raw Raw component value
+ * @v alpha Alpha value
+ * @v max Maximum raw/alpha value
+ * @ret value Component value in range 0-255
+ */
+static inline unsigned int png_pixel ( unsigned int raw, unsigned int alpha,
+ unsigned int max ) {
+
+ /* The basic calculation is 255*(raw/max)*(value/max). We use
+ * fixed-point arithmetic (scaling up to the maximum range for
+ * a 32-bit integer), in order to get the same results for
+ * alpha blending as the test cases (produced using
+ * ImageMagick).
+ */
+ return ( ( ( ( ( 0xff00 * raw * alpha ) / max ) / max ) + 0x80 ) >> 8 );
+}
+
+/**
+ * Fill one interlace pass of PNG pixels
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v interlace Interlace pass
+ *
+ * This routine may assume that it is impossible to overrun either the
+ * raw data buffer or the pixel buffer, since the sizes of both are
+ * determined by the image dimensions.
+ */
+static void png_pixels_pass ( struct image *image,
+ struct png_context *png,
+ struct png_interlace *interlace ) {
+ size_t raw_offset = png->raw.offset;
+ uint8_t channel[png->channels];
+ int is_indexed = ( png->colour_type & PNG_COLOUR_TYPE_PALETTE );
+ int is_rgb = ( png->colour_type & PNG_COLOUR_TYPE_RGB );
+ int has_alpha = ( png->colour_type & PNG_COLOUR_TYPE_ALPHA );
+ size_t pixbuf_y_offset;
+ size_t pixbuf_offset;
+ size_t pixbuf_x_stride;
+ size_t pixbuf_y_stride;
+ size_t raw_stride;
+ unsigned int y;
+ unsigned int x;
+ unsigned int c;
+ unsigned int bits;
+ unsigned int depth;
+ unsigned int max;
+ unsigned int alpha;
+ unsigned int raw;
+ unsigned int value;
+ uint8_t current = 0;
+ uint32_t pixel;
+
+ /* We only ever use the top byte of 16-bit pixels. Model this
+ * as a bit depth of 8 with a stride of more than one.
+ */
+ depth = png->depth;
+ raw_stride = ( ( depth + 7 ) / 8 );
+ if ( depth > 8 )
+ depth = 8;
+ max = ( ( 1 << depth ) - 1 );
+
+ /* Calculate pixel buffer offset and strides */
+ pixbuf_y_offset = ( ( ( interlace->y_indent * png->pixbuf->width ) +
+ interlace->x_indent ) * sizeof ( pixel ) );
+ pixbuf_x_stride = ( interlace->x_stride * sizeof ( pixel ) );
+ pixbuf_y_stride = ( interlace->y_stride * png->pixbuf->width *
+ sizeof ( pixel ) );
+ DBGC2 ( image, "PNG %s pass %d %dx%d at (%d,%d) stride (%d,%d)\n",
+ image->name, interlace->pass, interlace->width,
+ interlace->height, interlace->x_indent, interlace->y_indent,
+ interlace->x_stride, interlace->y_stride );
+
+ /* Iterate over each scanline in turn */
+ for ( y = 0 ; y < interlace->height ; y++ ) {
+
+ /* Skip filter byte */
+ raw_offset++;
+
+ /* Iterate over each pixel in turn */
+ bits = depth;
+ pixbuf_offset = pixbuf_y_offset;
+ for ( x = 0 ; x < interlace->width ; x++ ) {
+
+ /* Extract sample value */
+ for ( c = 0 ; c < png->channels ; c++ ) {
+
+ /* Get sample value into high bits of current */
+ current <<= depth;
+ bits -= depth;
+ if ( ! bits ) {
+ copy_from_user ( &current,
+ png->raw.data,
+ raw_offset,
+ sizeof ( current ) );
+ raw_offset += raw_stride;
+ bits = 8;
+ }
+
+ /* Extract sample value */
+ channel[c] = ( current >> ( 8 - depth ) );
+ }
+
+ /* Convert to native pixel format */
+ if ( is_indexed ) {
+
+ /* Indexed */
+ pixel = png->palette[channel[0]];
+
+ } else {
+
+ /* Determine alpha value */
+ alpha = ( has_alpha ?
+ channel[ png->channels - 1 ] : max );
+
+ /* Convert to RGB value */
+ pixel = 0;
+ for ( c = 0 ; c < 3 ; c++ ) {
+ raw = channel[ is_rgb ? c : 0 ];
+ value = png_pixel ( raw, alpha, max );
+ assert ( value <= 255 );
+ pixel = ( ( pixel << 8 ) | value );
+ }
+ }
+
+ /* Store pixel */
+ copy_to_user ( png->pixbuf->data, pixbuf_offset,
+ &pixel, sizeof ( pixel ) );
+ pixbuf_offset += pixbuf_x_stride;
+ }
+
+ /* Move to next output row */
+ pixbuf_y_offset += pixbuf_y_stride;
+ }
+
+ /* Update offset */
+ png->raw.offset = raw_offset;
+}
+
+/**
+ * Fill PNG pixels
+ *
+ * @v image PNG image
+ * @v png PNG context
+ *
+ * This routine may assume that it is impossible to overrun either the
+ * raw data buffer or the pixel buffer, since the sizes of both are
+ * determined by the image dimensions.
+ */
+static void png_pixels ( struct image *image, struct png_context *png ) {
+ struct png_interlace interlace;
+ unsigned int pass;
+
+ /* Process each interlace pass */
+ png->raw.offset = 0;
+ for ( pass = 0 ; pass < png->passes ; pass++ ) {
+
+ /* Calculate interlace pass parameters */
+ png_interlace ( png, pass, &interlace );
+
+ /* Skip zero-width rows (which have no filter bytes) */
+ if ( interlace.width == 0 )
+ continue;
+
+ /* Unfilter this pass */
+ png_pixels_pass ( image, png, &interlace );
+ }
+ assert ( png->raw.offset == png->raw.len );
+}
+
+/**
+ * Handle PNG image end chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+static int png_image_end ( struct image *image, struct png_context *png,
+ size_t len ) {
+ int rc;
+
+ /* Sanity checks */
+ if ( len != 0 ) {
+ DBGC ( image, "PNG %s invalid IEND length %zd\n",
+ image->name, len );
+ return -EINVAL;
+ }
+ if ( ! png->pixbuf ) {
+ DBGC ( image, "PNG %s missing pixel buffer (no IHDR?)\n",
+ image->name );
+ return -EINVAL;
+ }
+ if ( ! deflate_finished ( &png->deflate ) ) {
+ DBGC ( image, "PNG %s decompression not complete\n",
+ image->name );
+ return -EINVAL;
+ }
+ if ( png->raw.offset != png->raw.len ) {
+ DBGC ( image, "PNG %s incorrect decompressed length (expected "
+ "%zd, got %zd)\n", image->name, png->raw.len,
+ png->raw.offset );
+ return -EINVAL;
+ }
+
+ /* Unfilter raw data */
+ if ( ( rc = png_unfilter ( image, png ) ) != 0 )
+ return rc;
+
+ /* Fill pixel buffer */
+ png_pixels ( image, png );
+
+ return 0;
+}
+
+/** A PNG chunk handler */
+struct png_chunk_handler {
+ /** Chunk type */
+ uint32_t type;
+ /**
+ * Handle chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+ int ( * handle ) ( struct image *image, struct png_context *png,
+ size_t len );
+};
+
+/** PNG chunk handlers */
+static struct png_chunk_handler png_chunk_handlers[] = {
+ { htonl ( PNG_TYPE_IHDR ), png_image_header },
+ { htonl ( PNG_TYPE_PLTE ), png_palette },
+ { htonl ( PNG_TYPE_IDAT ), png_image_data },
+ { htonl ( PNG_TYPE_IEND ), png_image_end },
+};
+
+/**
+ * Handle PNG chunk
+ *
+ * @v image PNG image
+ * @v png PNG context
+ * @v type Chunk type
+ * @v len Chunk length
+ * @ret rc Return status code
+ */
+static int png_chunk ( struct image *image, struct png_context *png,
+ uint32_t type, size_t len ) {
+ struct png_chunk_handler *handler;
+ unsigned int i;
+
+ DBGC ( image, "PNG %s chunk type %s offset %zd length %zd\n",
+ image->name, png_type_name ( type ), png->offset, len );
+
+ /* Handle according to chunk type */
+ for ( i = 0 ; i < ( sizeof ( png_chunk_handlers ) /
+ sizeof ( png_chunk_handlers[0] ) ) ; i++ ) {
+ handler = &png_chunk_handlers[i];
+ if ( handler->type == type )
+ return handler->handle ( image, png, len );
+ }
+
+ /* Fail if unknown chunk type is critical */
+ if ( ! ( type & htonl ( PNG_CHUNK_ANCILLARY ) ) ) {
+ DBGC ( image, "PNG %s unknown critical chunk type %s\n",
+ image->name, png_type_name ( type ) );
+ return -ENOTSUP;
+ }
+
+ /* Ignore non-critical unknown chunk types */
+ return 0;
+}
+
+/**
+ * Convert PNG image to pixel buffer
+ *
+ * @v image PNG image
+ * @v pixbuf Pixel buffer to fill in
+ * @ret rc Return status code
+ */
+static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
+ struct png_context *png;
+ struct png_chunk_header header;
+ struct png_chunk_footer footer;
+ size_t remaining;
+ size_t chunk_len;
+ int rc;
+
+ /* Allocate and initialise context */
+ png = zalloc ( sizeof ( *png ) );
+ if ( ! png ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ png->offset = sizeof ( struct png_signature );
+ deflate_init ( &png->deflate, DEFLATE_ZLIB );
+
+ /* Process chunks */
+ do {
+
+ /* Extract chunk header */
+ remaining = ( image->len - png->offset );
+ if ( remaining < sizeof ( header ) ) {
+ DBGC ( image, "PNG %s truncated chunk header at offset "
+ "%zd\n", image->name, png->offset );
+ rc = -EINVAL;
+ goto err_truncated;
+ }
+ copy_from_user ( &header, image->data, png->offset,
+ sizeof ( header ) );
+ png->offset += sizeof ( header );
+
+ /* Validate chunk length */
+ chunk_len = ntohl ( header.len );
+ if ( remaining < ( sizeof ( header ) + chunk_len +
+ sizeof ( footer ) ) ) {
+ DBGC ( image, "PNG %s truncated chunk data/footer at "
+ "offset %zd\n", image->name, png->offset );
+ rc = -EINVAL;
+ goto err_truncated;
+ }
+
+ /* Handle chunk */
+ if ( ( rc = png_chunk ( image, png, header.type,
+ chunk_len ) ) != 0 )
+ goto err_chunk;
+
+ /* Move to next chunk */
+ png->offset += ( chunk_len + sizeof ( footer ) );
+
+ } while ( png->offset < image->len );
+
+ /* Check that we finished with an IEND chunk */
+ if ( header.type != htonl ( PNG_TYPE_IEND ) ) {
+ DBGC ( image, "PNG %s did not finish with IEND\n",
+ image->name );
+ rc = -EINVAL;
+ goto err_iend;
+ }
+
+ /* Return pixel buffer */
+ *pixbuf = pixbuf_get ( png->pixbuf );
+
+ /* Success */
+ rc = 0;
+
+ err_iend:
+ err_chunk:
+ err_truncated:
+ pixbuf_put ( png->pixbuf );
+ ufree ( png->raw.data );
+ free ( png );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Probe PNG image
+ *
+ * @v image PNG image
+ * @ret rc Return status code
+ */
+static int png_probe ( struct image *image ) {
+ struct png_signature signature;
+
+ /* Sanity check */
+ if ( image->len < sizeof ( signature ) ) {
+ DBGC ( image, "PNG %s is too short\n", image->name );
+ return -ENOEXEC;
+ }
+
+ /* Check signature */
+ copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
+ if ( memcmp ( &signature, &png_signature, sizeof ( signature ) ) != 0 ){
+ DBGC ( image, "PNG %s has invalid signature\n", image->name );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/** PNG image type */
+struct image_type png_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "PNG",
+ .probe = png_probe,
+ .pixbuf = png_pixbuf,
+};
diff --git a/qemu/roms/ipxe/src/image/pnm.c b/qemu/roms/ipxe/src/image/pnm.c
new file mode 100644
index 000000000..af9e571a2
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/pnm.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2013 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 );
+
+/** @file
+ *
+ * Portable anymap format (PNM)
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <ipxe/image.h>
+#include <ipxe/pixbuf.h>
+#include <ipxe/pnm.h>
+
+/**
+ * Extract PNM ASCII value
+ *
+ * @v image PNM image
+ * @v pnm PNM context
+ * @ret value Value, or negative error
+ */
+static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
+ char buf[ pnm->ascii_len + 1 /* NUL */ ];
+ char *endp;
+ size_t len;
+ int value;
+ int in_comment = 0;
+
+ /* Skip any leading whitespace and comments */
+ for ( ; pnm->offset < image->len ; pnm->offset++ ) {
+ copy_from_user ( &buf[0], image->data, pnm->offset,
+ sizeof ( buf[0] ) );
+ if ( in_comment ) {
+ if ( buf[0] == '\n' )
+ in_comment = 0;
+ } else {
+ if ( buf[0] == '#' ) {
+ in_comment = 1;
+ } else if ( ! isspace ( buf[0] ) ) {
+ break;
+ }
+ }
+ }
+
+ /* Fail if no value is present */
+ len = ( image->len - pnm->offset );
+ if ( len == 0 ) {
+ DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
+ return -EINVAL;
+ }
+
+ /* Copy ASCII value to buffer and ensure string is NUL-terminated */
+ if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) )
+ len = ( sizeof ( buf ) - 1 /* NUL */ );
+ copy_from_user ( buf, image->data, pnm->offset, len );
+ buf[len] = '\0';
+
+ /* Parse value and update offset */
+ value = strtoul ( buf, &endp, 0 );
+ pnm->offset += ( endp - buf );
+
+ /* Check and skip terminating whitespace character, if present */
+ if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) {
+ if ( ! isspace ( *endp ) ) {
+ DBGC ( image, "PNM %s invalid ASCII integer\n",
+ image->name );
+ return -EINVAL;
+ }
+ pnm->offset++;
+ }
+
+ return value;
+}
+
+/**
+ * Extract PNM binary value
+ *
+ * @v image PNM image
+ * @v pnm PNM context
+ * @ret value Value, or negative error
+ */
+static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
+ uint8_t value;
+
+ /* Sanity check */
+ if ( pnm->offset == image->len ) {
+ DBGC ( image, "PNM %s ran out of binary data\n",
+ image->name );
+ return -EINVAL;
+ }
+
+ /* Extract value */
+ copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
+ pnm->offset++;
+
+ return value;
+}
+
+/**
+ * Scale PNM scalar value
+ *
+ * @v image PNM image
+ * @v pnm PNM context
+ * @v value Raw value
+ * @ret value Scaled value (in range 0-255)
+ */
+static int pnm_scale ( struct image *image, struct pnm_context *pnm,
+ unsigned int value ) {
+
+ if ( value > pnm->max ) {
+ DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n",
+ image->name, value, pnm->max );
+ return -EINVAL;
+ }
+ return ( ( 255 * value ) / pnm->max );
+}
+
+/**
+ * Convert PNM bitmap composite value to RGB
+ *
+ * @v composite Composite value
+ * @v index Pixel index within this composite value
+ * @ret rgb 24-bit RGB value
+ */
+static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
+
+ /* Composite value is an 8-bit bitmask */
+ return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
+}
+
+/**
+ * Convert PNM greymap composite value to RGB
+ *
+ * @v composite Composite value
+ * @v index Pixel index within this composite value
+ * @ret rgb 24-bit RGB value
+ */
+static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
+
+ /* Composite value is an 8-bit greyscale value */
+ return ( ( composite << 16 ) | ( composite << 8 ) | composite );
+}
+
+/**
+ * Convert PNM pixmap composite value to RGB
+ *
+ * @v composite Composite value
+ * @v index Pixel index within this composite value
+ * @ret rgb 24-bit RGB value
+ */
+static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
+
+ /* Composite value is already an RGB value */
+ return composite;
+}
+
+/**
+ * Extract PNM pixel data
+ *
+ * @v image PNM image
+ * @v pnm PNM context
+ * @v pixbuf Pixel buffer
+ * @ret rc Return status code
+ */
+static int pnm_data ( struct image *image, struct pnm_context *pnm,
+ struct pixel_buffer *pixbuf ) {
+ struct pnm_type *type = pnm->type;
+ size_t offset = 0;
+ unsigned int xpos = 0;
+ int scalar;
+ uint32_t composite;
+ uint32_t rgb;
+ unsigned int i;
+
+ /* Fill pixel buffer */
+ while ( offset < pixbuf->len ) {
+
+ /* Extract a scaled composite scalar value from the file */
+ composite = 0;
+ for ( i = 0 ; i < type->depth ; i++ ) {
+ scalar = type->scalar ( image, pnm );
+ if ( scalar < 0 )
+ return scalar;
+ scalar = pnm_scale ( image, pnm, scalar );
+ if ( scalar < 0 )
+ return scalar;
+ composite = ( ( composite << 8 ) | scalar );
+ }
+
+ /* Extract 24-bit RGB values from composite value */
+ for ( i = 0 ; i < type->packing ; i++ ) {
+ if ( offset >= pixbuf->len ) {
+ DBGC ( image, "PNM %s has too many pixels\n",
+ image->name );
+ return -EINVAL;
+ }
+ rgb = type->rgb ( composite, i );
+ copy_to_user ( pixbuf->data, offset, &rgb,
+ sizeof ( rgb ) );
+ offset += sizeof ( rgb );
+ if ( ++xpos == pixbuf->width ) {
+ xpos = 0;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** PNM image types */
+static struct pnm_type pnm_types[] = {
+ {
+ .type = '1',
+ .depth = 1,
+ .packing = 1,
+ .flags = PNM_BITMAP,
+ .scalar = pnm_ascii,
+ .rgb = pnm_bitmap,
+ },
+ {
+ .type = '2',
+ .depth = 1,
+ .packing = 1,
+ .scalar = pnm_ascii,
+ .rgb = pnm_greymap,
+ },
+ {
+ .type = '3',
+ .depth = 3,
+ .packing = 1,
+ .scalar = pnm_ascii,
+ .rgb = pnm_pixmap,
+ },
+ {
+ .type = '4',
+ .depth = 1,
+ .packing = 8,
+ .flags = PNM_BITMAP,
+ .scalar = pnm_binary,
+ .rgb = pnm_bitmap,
+ },
+ {
+ .type = '5',
+ .depth = 1,
+ .packing = 1,
+ .scalar = pnm_binary,
+ .rgb = pnm_greymap,
+ },
+ {
+ .type = '6',
+ .depth = 3,
+ .packing = 1,
+ .scalar = pnm_binary,
+ .rgb = pnm_pixmap,
+ },
+};
+
+/**
+ * Determine PNM image type
+ *
+ * @v image PNM image
+ * @ret type PNM image type, or NULL if not found
+ */
+static struct pnm_type * pnm_type ( struct image *image ) {
+ struct pnm_signature signature;
+ struct pnm_type *type;
+ unsigned int i;
+
+ /* Extract signature */
+ assert ( image->len >= sizeof ( signature ) );
+ copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
+
+ /* Check for supported types */
+ for ( i = 0 ; i < ( sizeof ( pnm_types ) /
+ sizeof ( pnm_types[0] ) ) ; i++ ) {
+ type = &pnm_types[i];
+ if ( type->type == signature.type )
+ return type;
+ }
+ return NULL;
+}
+
+/**
+ * Convert PNM image to pixel buffer
+ *
+ * @v image PNM image
+ * @v pixbuf Pixel buffer to fill in
+ * @ret rc Return status code
+ */
+static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
+ struct pnm_context pnm;
+ int width;
+ int height;
+ int max;
+ int rc;
+
+ /* Initialise PNM context */
+ pnm.type = pnm_type ( image );
+ if ( ! pnm.type ) {
+ rc = -ENOTSUP;
+ goto err_type;
+ }
+ pnm.offset = sizeof ( struct pnm_signature );
+ pnm.ascii_len = PNM_ASCII_LEN;
+
+ /* Extract width */
+ if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
+ rc = width;
+ goto err_width;
+ }
+
+ /* Extract height */
+ if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
+ rc = height;
+ goto err_height;
+ }
+
+ /* Extract maximum scalar value, if not predefined */
+ if ( pnm.type->flags & PNM_BITMAP ) {
+ pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
+ pnm.ascii_len = 1;
+ } else {
+ if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
+ rc = max;
+ goto err_max;
+ }
+ pnm.max = max;
+ }
+ if ( pnm.max == 0 ) {
+ DBGC ( image, "PNM %s has invalid maximum value 0\n",
+ image->name );
+ rc = -EINVAL;
+ goto err_max;
+ }
+ DBGC ( image, "PNM %s is type %c width %d height %d max %d\n",
+ image->name, pnm.type->type, width, height, pnm.max );
+
+ /* Allocate pixel buffer */
+ *pixbuf = alloc_pixbuf ( width, height );
+ if ( ! *pixbuf ) {
+ rc = -ENOMEM;
+ goto err_alloc_pixbuf;
+ }
+
+ /* Extract pixel data */
+ if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
+ goto err_data;
+
+ return 0;
+
+ err_data:
+ pixbuf_put ( *pixbuf );
+ err_alloc_pixbuf:
+ err_max:
+ err_height:
+ err_width:
+ err_type:
+ return rc;
+}
+
+/**
+ * Probe PNM image
+ *
+ * @v image PNM image
+ * @ret rc Return status code
+ */
+static int pnm_probe ( struct image *image ) {
+ struct pnm_signature signature;
+
+ /* Sanity check */
+ if ( image->len < sizeof ( signature ) ) {
+ DBGC ( image, "PNM %s is too short\n", image->name );
+ return -ENOEXEC;
+ }
+
+ /* Check signature */
+ copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
+ if ( ! ( ( signature.magic == PNM_MAGIC ) &&
+ ( isdigit ( signature.type ) ) &&
+ ( isspace ( signature.space ) ) ) ) {
+ DBGC ( image, "PNM %s has invalid signature\n", image->name );
+ return -ENOEXEC;
+ }
+ DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
+
+ return 0;
+}
+
+/** PNM image type */
+struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "PNM",
+ .probe = pnm_probe,
+ .pixbuf = pnm_pixbuf,
+};
diff --git a/qemu/roms/ipxe/src/image/script.c b/qemu/roms/ipxe/src/image/script.c
new file mode 100644
index 000000000..5328da8b4
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/script.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2007 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 );
+
+/**
+ * @file
+ *
+ * iPXE scripts
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ipxe/command.h>
+#include <ipxe/parseopt.h>
+#include <ipxe/image.h>
+#include <ipxe/shell.h>
+#include <usr/prompt.h>
+#include <ipxe/script.h>
+
+/** Offset within current script
+ *
+ * This is a global in order to allow goto_exec() to update the
+ * offset.
+ */
+static size_t script_offset;
+
+/**
+ * Process script lines
+ *
+ * @v image Script
+ * @v process_line Line processor
+ * @v terminate Termination check
+ * @ret rc Return status code
+ */
+static int process_script ( struct image *image,
+ int ( * process_line ) ( struct image *image,
+ size_t offset,
+ const char *label,
+ const char *command ),
+ int ( * terminate ) ( int rc ) ) {
+ size_t len = 0;
+ char *line = NULL;
+ size_t line_offset;
+ char *label;
+ char *command;
+ off_t eol;
+ size_t frag_len;
+ char *tmp;
+ int rc;
+
+ /* Initialise script and line offsets */
+ script_offset = 0;
+ line_offset = 0;
+
+ do {
+
+ /* Find length of next line, excluding any terminating '\n' */
+ eol = memchr_user ( image->data, script_offset, '\n',
+ ( image->len - script_offset ) );
+ if ( eol < 0 )
+ eol = image->len;
+ frag_len = ( eol - script_offset );
+
+ /* Allocate buffer for line */
+ tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) );
+ if ( ! tmp ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ line = tmp;
+
+ /* Copy line */
+ copy_from_user ( ( line + len ), image->data, script_offset,
+ frag_len );
+ len += frag_len;
+
+ /* Move to next line in script */
+ script_offset += ( frag_len + 1 );
+
+ /* Strip trailing CR, if present */
+ if ( len && ( line[ len - 1 ] == '\r' ) )
+ len--;
+
+ /* Handle backslash continuations */
+ if ( len && ( line[ len - 1 ] == '\\' ) ) {
+ len--;
+ rc = -EINVAL;
+ continue;
+ }
+
+ /* Terminate line */
+ line[len] = '\0';
+
+ /* Split line into (optional) label and command */
+ command = line;
+ while ( isspace ( *command ) )
+ command++;
+ if ( *command == ':' ) {
+ label = ++command;
+ while ( *command && ! isspace ( *command ) )
+ command++;
+ if ( *command )
+ *(command++) = '\0';
+ } else {
+ label = NULL;
+ }
+
+ /* Process line */
+ rc = process_line ( image, line_offset, label, command );
+ if ( terminate ( rc ) )
+ goto err_process;
+
+ /* Free line */
+ free ( line );
+ line = NULL;
+ len = 0;
+
+ /* Update line offset */
+ line_offset = script_offset;
+
+ } while ( script_offset < image->len );
+
+ err_process:
+ err_alloc:
+ free ( line );
+ return rc;
+}
+
+/**
+ * Terminate script processing on shell exit or command failure
+ *
+ * @v rc Line processing status
+ * @ret terminate Terminate script processing
+ */
+static int terminate_on_exit_or_failure ( int rc ) {
+
+ return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ||
+ ( rc != 0 ) );
+}
+
+/**
+ * Execute script line
+ *
+ * @v image Script
+ * @v offset Offset within script
+ * @v label Label, or NULL
+ * @v command Command
+ * @ret rc Return status code
+ */
+static int script_exec_line ( struct image *image, size_t offset,
+ const char *label __unused,
+ const char *command ) {
+ int rc;
+
+ DBGC ( image, "[%04zx] $ %s\n", offset, command );
+
+ /* Execute command */
+ if ( ( rc = system ( command ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Execute script
+ *
+ * @v image Script
+ * @ret rc Return status code
+ */
+static int script_exec ( struct image *image ) {
+ size_t saved_offset;
+ int rc;
+
+ /* Temporarily de-register image, so that a "boot" command
+ * doesn't throw us into an execution loop.
+ */
+ unregister_image ( image );
+
+ /* Preserve state of any currently-running script */
+ saved_offset = script_offset;
+
+ /* Process script */
+ rc = process_script ( image, script_exec_line,
+ terminate_on_exit_or_failure );
+
+ /* Restore saved state */
+ script_offset = saved_offset;
+
+ /* Re-register image (unless we have been replaced) */
+ if ( ! image->replacement )
+ register_image ( image );
+
+ return rc;
+}
+
+/**
+ * Probe script image
+ *
+ * @v image Script
+ * @ret rc Return status code
+ */
+static int script_probe ( struct image *image ) {
+ static const char ipxe_magic[] = "#!ipxe";
+ static const char gpxe_magic[] = "#!gpxe";
+ linker_assert ( sizeof ( ipxe_magic ) == sizeof ( gpxe_magic ),
+ magic_size_mismatch );
+ char test[ sizeof ( ipxe_magic ) - 1 /* NUL */
+ + 1 /* terminating space */];
+
+ /* Sanity check */
+ if ( image->len < sizeof ( test ) ) {
+ DBGC ( image, "Too short to be a script\n" );
+ return -ENOEXEC;
+ }
+
+ /* Check for magic signature */
+ copy_from_user ( test, image->data, 0, sizeof ( test ) );
+ if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) ||
+ ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) &&
+ isspace ( test[ sizeof ( test ) - 1 ] ) ) ) {
+ DBGC ( image, "Invalid magic signature\n" );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/** Script image type */
+struct image_type script_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "script",
+ .probe = script_probe,
+ .exec = script_exec,
+};
+
+/** "goto" options */
+struct goto_options {};
+
+/** "goto" option list */
+static struct option_descriptor goto_opts[] = {};
+
+/** "goto" command descriptor */
+static struct command_descriptor goto_cmd =
+ COMMAND_DESC ( struct goto_options, goto_opts, 1, 1, "<label>" );
+
+/**
+ * Current "goto" label
+ *
+ * Valid only during goto_exec(). Consider this part of a closure.
+ */
+static const char *goto_label;
+
+/**
+ * Check for presence of label
+ *
+ * @v image Script
+ * @v offset Offset within script
+ * @v label Label
+ * @v command Command
+ * @ret rc Return status code
+ */
+static int goto_find_label ( struct image *image, size_t offset,
+ const char *label, const char *command __unused ) {
+
+ /* Check label exists */
+ if ( ! label )
+ return -ENOENT;
+
+ /* Check label matches */
+ if ( strcmp ( goto_label, label ) != 0 )
+ return -ENOENT;
+
+ /* Update script offset */
+ script_offset = offset;
+ DBGC ( image, "[%04zx] Gone to :%s\n", offset, label );
+
+ return 0;
+}
+
+/**
+ * Terminate script processing when label is found
+ *
+ * @v rc Line processing status
+ * @ret terminate Terminate script processing
+ */
+static int terminate_on_label_found ( int rc ) {
+ return ( rc == 0 );
+}
+
+/**
+ * "goto" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+static int goto_exec ( int argc, char **argv ) {
+ struct goto_options opts;
+ size_t saved_offset;
+ int rc;
+
+ /* Parse options */
+ if ( ( rc = parse_options ( argc, argv, &goto_cmd, &opts ) ) != 0 )
+ return rc;
+
+ /* Sanity check */
+ if ( ! current_image ) {
+ rc = -ENOTTY;
+ printf ( "Not in a script: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Parse label */
+ goto_label = argv[optind];
+
+ /* Find label */
+ saved_offset = script_offset;
+ if ( ( rc = process_script ( current_image, goto_find_label,
+ terminate_on_label_found ) ) != 0 ) {
+ script_offset = saved_offset;
+ DBGC ( current_image, "[%04zx] No such label :%s\n",
+ script_offset, goto_label );
+ return rc;
+ }
+
+ /* Terminate processing of current command */
+ shell_stop ( SHELL_STOP_COMMAND );
+
+ return 0;
+}
+
+/** "goto" command */
+struct command goto_command __command = {
+ .name = "goto",
+ .exec = goto_exec,
+};
+
+/** "prompt" options */
+struct prompt_options {
+ /** Key to wait for */
+ unsigned int key;
+ /** Timeout */
+ unsigned long timeout;
+};
+
+/** "prompt" option list */
+static struct option_descriptor prompt_opts[] = {
+ OPTION_DESC ( "key", 'k', required_argument,
+ struct prompt_options, key, parse_key ),
+ OPTION_DESC ( "timeout", 't', required_argument,
+ struct prompt_options, timeout, parse_timeout ),
+};
+
+/** "prompt" command descriptor */
+static struct command_descriptor prompt_cmd =
+ COMMAND_DESC ( struct prompt_options, prompt_opts, 0, MAX_ARGUMENTS,
+ "[<text>]" );
+
+/**
+ * "prompt" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+static int prompt_exec ( int argc, char **argv ) {
+ struct prompt_options opts;
+ char *text;
+ int rc;
+
+ /* Parse options */
+ if ( ( rc = parse_options ( argc, argv, &prompt_cmd, &opts ) ) != 0 )
+ goto err_parse;
+
+ /* Parse prompt text */
+ text = concat_args ( &argv[optind] );
+ if ( ! text ) {
+ rc = -ENOMEM;
+ goto err_concat;
+ }
+
+ /* Display prompt and wait for key */
+ if ( ( rc = prompt ( text, opts.timeout, opts.key ) ) != 0 )
+ goto err_prompt;
+
+ /* Free prompt text */
+ free ( text );
+
+ return 0;
+
+ err_prompt:
+ free ( text );
+ err_concat:
+ err_parse:
+ return rc;
+}
+
+/** "prompt" command */
+struct command prompt_command __command = {
+ .name = "prompt",
+ .exec = prompt_exec,
+};
diff --git a/qemu/roms/ipxe/src/image/segment.c b/qemu/roms/ipxe/src/image/segment.c
new file mode 100644
index 000000000..86fe42662
--- /dev/null
+++ b/qemu/roms/ipxe/src/image/segment.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 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 );
+
+/**
+ * @file
+ *
+ * Executable image segments
+ *
+ */
+
+#include <errno.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/io.h>
+#include <ipxe/errortab.h>
+#include <ipxe/segment.h>
+
+/**
+ * Segment-specific error messages
+ *
+ * This error happens sufficiently often to merit a user-friendly
+ * description.
+ */
+#define ERANGE_SEGMENT __einfo_error ( EINFO_ERANGE_SEGMENT )
+#define EINFO_ERANGE_SEGMENT \
+ __einfo_uniqify ( EINFO_ERANGE, 0x01, "Requested memory not available" )
+struct errortab segment_errors[] __errortab = {
+ __einfo_errortab ( EINFO_ERANGE_SEGMENT ),
+};
+
+/**
+ * Prepare segment for loading
+ *
+ * @v segment Segment start
+ * @v filesz Size of the "allocated bytes" portion of the segment
+ * @v memsz Size of the segment
+ * @ret rc Return status code
+ */
+int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) {
+ struct memory_map memmap;
+ physaddr_t start = user_to_phys ( segment, 0 );
+ physaddr_t mid = user_to_phys ( segment, filesz );
+ physaddr_t end = user_to_phys ( segment, memsz );
+ unsigned int i;
+
+ DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end );
+
+ /* Sanity check */
+ if ( filesz > memsz ) {
+ DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end );
+ return -EINVAL;
+ }
+
+ /* Get a fresh memory map. This allows us to automatically
+ * avoid treading on any regions that Etherboot is currently
+ * editing out of the memory map.
+ */
+ get_memmap ( &memmap );
+
+ /* Look for a suitable memory region */
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ if ( ( start >= memmap.regions[i].start ) &&
+ ( end <= memmap.regions[i].end ) ) {
+ /* Found valid region: zero bss and return */
+ memset_user ( segment, filesz, 0, ( memsz - filesz ) );
+ return 0;
+ }
+ }
+
+ /* No suitable memory region found */
+ DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n",
+ start, mid, end );
+ return -ERANGE_SEGMENT;
+}