summaryrefslogtreecommitdiffstats
path: root/src/image
diff options
context:
space:
mode:
authorSimon Rettberg2026-01-28 12:53:53 +0100
committerSimon Rettberg2026-01-28 12:53:53 +0100
commit8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch)
treea8b359e59196be5b2e3862bed189107f4bc9975f /src/image
parentMerge branch 'master' into openslx (diff)
parent[prefix] Make unlzma.S compatible with 386 class CPUs (diff)
downloadipxe-openslx.tar.gz
ipxe-openslx.tar.xz
ipxe-openslx.zip
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/image')
-rw-r--r--src/image/der.c100
-rw-r--r--src/image/efi_image.c130
-rw-r--r--src/image/efi_siglist.c256
-rw-r--r--src/image/elf.c75
-rw-r--r--src/image/embedded.c24
-rw-r--r--src/image/gzip.c116
-rw-r--r--src/image/initrd.c399
-rw-r--r--src/image/lkrn.c368
-rw-r--r--src/image/pem.c25
-rw-r--r--src/image/png.c185
-rw-r--r--src/image/pnm.c56
-rw-r--r--src/image/script.c47
-rw-r--r--src/image/segment.c62
-rw-r--r--src/image/zlib.c46
14 files changed, 1480 insertions, 409 deletions
diff --git a/src/image/der.c b/src/image/der.c
index fa17e5659..ace106b84 100644
--- a/src/image/der.c
+++ b/src/image/der.c
@@ -22,13 +22,14 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/asn1.h>
#include <ipxe/der.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
/** @file
@@ -38,32 +39,41 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
- * Extract ASN.1 object from image
+ * Extract ASN.1 object from DER data
*
- * @v image DER image
- * @v offset Offset within image
+ * @v data DER data
+ * @v len Length of DER data
+ * @v offset Offset within data
* @v cursor ASN.1 cursor to fill in
- * @ret next Offset to next image, or negative error
+ * @ret next Offset to next object, or negative error
*
* The caller is responsible for eventually calling free() on the
* allocated ASN.1 cursor.
*/
-static int der_asn1 ( struct image *image, size_t offset __unused,
- struct asn1_cursor **cursor ) {
- void *data;
+int der_asn1 ( const void *data, size_t len, size_t offset,
+ struct asn1_cursor **cursor ) {
+ size_t remaining;
+ void *raw;
+
+ /* Sanity check */
+ assert ( offset <= len );
+ remaining = ( len - offset );
/* Allocate cursor and data buffer */
- *cursor = malloc ( sizeof ( **cursor ) + image->len );
+ *cursor = malloc ( sizeof ( **cursor ) + remaining );
if ( ! *cursor )
return -ENOMEM;
- data = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) );
+ raw = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) );
/* Populate cursor and data buffer */
- (*cursor)->data = data;
- (*cursor)->len = image->len;
- copy_from_user ( data, image->data, 0, image->len );
+ (*cursor)->data = raw;
+ (*cursor)->len = remaining;
+ memcpy ( raw, ( data + offset ), remaining );
- return image->len;
+ /* Shrink cursor */
+ asn1_shrink_any ( *cursor );
+
+ return ( offset + (*cursor)->len );
}
/**
@@ -72,39 +82,23 @@ static int der_asn1 ( struct image *image, size_t offset __unused,
* @v image DER image
* @ret rc Return status code
*/
-static int der_probe ( struct image *image ) {
+static int der_image_probe ( struct image *image ) {
struct asn1_cursor cursor;
- uint8_t buf[8];
- size_t extra;
- size_t total;
- int len;
int rc;
- /* Sanity check: no realistic DER image can be smaller than this */
- if ( image->len < sizeof ( buf ) )
- return -ENOEXEC;
-
- /* Prepare partial cursor */
- cursor.data = buf;
- cursor.len = sizeof ( buf );
- copy_from_user ( buf, image->data, 0, sizeof ( buf ) );
- extra = ( image->len - sizeof ( buf ) );
+ /* Prepare cursor */
+ cursor.data = image->data;
+ cursor.len = image->len;
- /* Get length of ASN.1 sequence */
- len = asn1_start ( &cursor, ASN1_SEQUENCE, extra );
- if ( len < 0 ) {
- rc = len;
+ /* Check that image begins with an ASN.1 sequence object */
+ if ( ( rc = asn1_skip ( &cursor, ASN1_SEQUENCE ) ) != 0 ) {
DBGC ( image, "DER %s is not valid ASN.1: %s\n",
image->name, strerror ( rc ) );
return rc;
}
- /* Add length of tag and length bytes consumed by asn1_start() */
- total = ( len + ( cursor.data - ( ( void * ) buf ) ) );
- assert ( total <= image->len );
-
/* Check that image comprises a single well-formed ASN.1 object */
- if ( total != image->len ) {
+ if ( cursor.len ) {
DBGC ( image, "DER %s is not single ASN.1\n", image->name );
return -ENOEXEC;
}
@@ -112,9 +106,37 @@ static int der_probe ( struct image *image ) {
return 0;
}
+/**
+ * Extract ASN.1 object from DER image
+ *
+ * @v image DER image
+ * @v offset Offset within image
+ * @v cursor ASN.1 cursor to fill in
+ * @ret next Offset to next image, or negative error
+ *
+ * The caller is responsible for eventually calling free() on the
+ * allocated ASN.1 cursor.
+ */
+static int der_image_asn1 ( struct image *image, size_t offset,
+ struct asn1_cursor **cursor ) {
+ int next;
+ int rc;
+
+ /* Extract ASN.1 object */
+ if ( ( next = der_asn1 ( image->data, image->len, offset,
+ cursor ) ) < 0 ) {
+ rc = next;
+ DBGC ( image, "DER %s could not extract ASN.1: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return next;
+}
+
/** DER image type */
struct image_type der_image_type __image_type ( PROBE_NORMAL ) = {
.name = "DER",
- .probe = der_probe,
- .asn1 = der_asn1,
+ .probe = der_image_probe,
+ .asn1 = der_image_asn1,
};
diff --git a/src/image/efi_image.c b/src/image/efi_image.c
index 104753a85..2631530e7 100644
--- a/src/image/efi_image.c
+++ b/src/image/efi_image.c
@@ -18,9 +18,11 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <wchar.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_snp.h>
@@ -33,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_image.h>
#include <ipxe/efi/efi_shim.h>
+#include <ipxe/efi/efi_fdt.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
@@ -123,6 +126,25 @@ static wchar_t * efi_image_cmdline ( struct image *image ) {
}
/**
+ * Install EFI Flattened Device Tree table (when no FDT support is present)
+ *
+ * @v cmdline Command line, or NULL
+ * @ret rc Return status code
+ */
+__weak int efi_fdt_install ( const char *cmdline __unused ) {
+ return 0;
+}
+
+/**
+ * Uninstall EFI Flattened Device Tree table (when no FDT support is present)
+ *
+ * @ret rc Return status code
+ */
+__weak int efi_fdt_uninstall ( void ) {
+ return 0;
+}
+
+/**
* Execute EFI image
*
* @v image EFI image
@@ -132,12 +154,10 @@ 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_LOADED_IMAGE_PROTOCOL *loaded;
struct image *shim;
struct image *exec;
+ EFI_HANDLE device;
EFI_HANDLE handle;
EFI_MEMORY_TYPE type;
wchar_t *cmdline;
@@ -153,6 +173,7 @@ static int efi_image_exec ( struct image *image ) {
rc = -ENODEV;
goto err_no_snpdev;
}
+ device = snpdev->handle;
/* Use shim instead of directly executing image if applicable */
shim = ( efi_can_load ( image ) ?
@@ -170,26 +191,33 @@ static int efi_image_exec ( struct image *image ) {
goto err_register_image;
/* Install file I/O protocols */
- if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) {
+ if ( ( rc = efi_file_install ( device ) ) != 0 ) {
DBGC ( image, "EFIIMAGE %s could not install file protocol: "
"%s\n", image->name, strerror ( rc ) );
goto err_file_install;
}
/* Install PXE base code protocol */
- if ( ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){
+ if ( ( rc = efi_pxe_install ( device, snpdev->netdev ) ) != 0 ){
DBGC ( image, "EFIIMAGE %s could not install PXE protocol: "
"%s\n", image->name, strerror ( rc ) );
goto err_pxe_install;
}
/* Install iPXE download protocol */
- if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) {
+ if ( ( rc = efi_download_install ( device ) ) != 0 ) {
DBGC ( image, "EFIIMAGE %s could not install iPXE download "
"protocol: %s\n", image->name, strerror ( rc ) );
goto err_download_install;
}
+ /* Install Flattened Device Tree table */
+ if ( ( rc = efi_fdt_install ( image->cmdline ) ) != 0 ) {
+ DBGC ( image, "EFIIMAGE %s could not install FDT: %s\n",
+ image->name, strerror ( rc ) );
+ goto err_fdt_install;
+ }
+
/* Create device path for image */
path = efi_image_path ( exec, snpdev->path );
if ( ! path ) {
@@ -210,18 +238,22 @@ static int efi_image_exec ( struct image *image ) {
/* Install shim special handling if applicable */
if ( shim &&
- ( ( rc = efi_shim_install ( shim, snpdev->handle,
- &cmdline ) ) != 0 ) ){
+ ( ( rc = efi_shim_install ( shim, device, &cmdline ) ) != 0 ) ) {
DBGC ( image, "EFIIMAGE %s could not install shim handling: "
"%s\n", image->name, strerror ( rc ) );
goto err_shim_install;
}
- /* Attempt loading image */
+ /* Attempt loading image
+ *
+ * LoadImage() does not (allegedly) modify the image content,
+ * but requires a non-const pointer to SourceBuffer. We
+ * therefore use the .rwdata field rather than .data.
+ */
handle = NULL;
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path,
- user_to_virt ( exec->data, 0 ),
- exec->len, &handle ) ) != 0 ) {
+ exec->rwdata, exec->len,
+ &handle ) ) != 0 ) {
/* Not an EFI image */
rc = -EEFI_LOAD ( efirc );
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
@@ -234,45 +266,45 @@ static int efi_image_exec ( struct image *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 ) {
+ if ( ( rc = efi_open ( handle, &efi_loaded_image_protocol_guid,
+ &loaded ) ) != 0 ) {
/* 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 ) {
+ if ( loaded->DeviceHandle == NULL ) {
DBGC ( image, "EFIIMAGE %s filling in missing DeviceHandle\n",
image->name );
- loaded.image->DeviceHandle = snpdev->handle;
+ loaded->DeviceHandle = device;
}
/* 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 );
+ assert ( loaded->ParentHandle == efi_image_handle );
+ assert ( loaded->DeviceHandle == device );
+ assert ( loaded->LoadOptionsSize == 0 );
+ assert ( loaded->LoadOptions == NULL );
/* Record image code type */
- type = loaded.image->ImageCodeType;
+ type = loaded->ImageCodeType;
/* Set command line */
- loaded.image->LoadOptions = cmdline;
- loaded.image->LoadOptionsSize =
+ loaded->LoadOptions = cmdline;
+ loaded->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 );
+ efi_wrap_image ( handle );
/* Reset console since image will probably use it */
console_reset();
+ /* Assume that image may cause SNP device to be removed */
+ snpdev = NULL;
+
/* Start the image */
if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
rc = -EEFI_START ( efirc );
@@ -291,6 +323,7 @@ static int efi_image_exec ( struct image *image ) {
rc = 0;
err_start_image:
+ efi_unwrap();
efi_snp_claim();
err_open_protocol:
/* If there was no error, then the image must have been
@@ -318,11 +351,13 @@ static int efi_image_exec ( struct image *image ) {
err_cmdline:
free ( path );
err_image_path:
- efi_download_uninstall ( snpdev->handle );
+ efi_fdt_uninstall();
+ err_fdt_install:
+ efi_download_uninstall ( device );
err_download_install:
- efi_pxe_uninstall ( snpdev->handle );
+ efi_pxe_uninstall ( device );
err_pxe_install:
- efi_file_uninstall ( snpdev->handle );
+ efi_file_uninstall ( device );
err_file_install:
unregister_image ( image );
err_register_image:
@@ -348,11 +383,16 @@ static int efi_image_probe ( struct image *image ) {
EFI_STATUS efirc;
int rc;
- /* Attempt loading image */
+ /* Attempt loading image
+ *
+ * LoadImage() does not (allegedly) modify the image content,
+ * but requires a non-const pointer to SourceBuffer. We
+ * therefore use the .rwdata field rather than .data.
+ */
handle = NULL;
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, &empty_path,
- user_to_virt ( image->data, 0 ),
- image->len, &handle ) ) != 0 ) {
+ image->rwdata, image->len,
+ &handle ) ) != 0 ) {
/* Not an EFI image */
rc = -EEFI_LOAD ( efirc );
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
@@ -394,42 +434,40 @@ static int efi_pe_image_probe ( struct image *image ) {
const UINT16 magic = ( ( sizeof ( UINTN ) == sizeof ( uint32_t ) ) ?
EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC :
EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC );
- union {
- EFI_IMAGE_DOS_HEADER dos;
- EFI_IMAGE_OPTIONAL_HEADER_UNION pe;
- } u;
+ const EFI_IMAGE_DOS_HEADER *dos;
+ const EFI_IMAGE_OPTIONAL_HEADER_UNION *pe;
/* Check for existence of DOS header */
- if ( image->len < sizeof ( u.dos ) ) {
+ if ( image->len < sizeof ( *dos ) ) {
DBGC ( image, "EFIIMAGE %s too short for DOS header\n",
image->name );
return -ENOEXEC;
}
- copy_from_user ( &u.dos, image->data, 0, sizeof ( u.dos ) );
- if ( u.dos.e_magic != EFI_IMAGE_DOS_SIGNATURE ) {
+ dos = image->data;
+ if ( dos->e_magic != EFI_IMAGE_DOS_SIGNATURE ) {
DBGC ( image, "EFIIMAGE %s missing MZ signature\n",
image->name );
return -ENOEXEC;
}
/* Check for existence of PE header */
- if ( ( image->len < u.dos.e_lfanew ) ||
- ( ( image->len - u.dos.e_lfanew ) < sizeof ( u.pe ) ) ) {
+ if ( ( image->len < dos->e_lfanew ) ||
+ ( ( image->len - dos->e_lfanew ) < sizeof ( *pe ) ) ) {
DBGC ( image, "EFIIMAGE %s too short for PE header\n",
image->name );
return -ENOEXEC;
}
- copy_from_user ( &u.pe, image->data, u.dos.e_lfanew, sizeof ( u.pe ) );
- if ( u.pe.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) {
+ pe = ( image->data + dos->e_lfanew );
+ if ( pe->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) {
DBGC ( image, "EFIIMAGE %s missing PE signature\n",
image->name );
return -ENOEXEC;
}
/* Check PE header magic */
- if ( u.pe.Pe32.OptionalHeader.Magic != magic ) {
+ if ( pe->Pe32.OptionalHeader.Magic != magic ) {
DBGC ( image, "EFIIMAGE %s incorrect magic %04x\n",
- image->name, u.pe.Pe32.OptionalHeader.Magic );
+ image->name, pe->Pe32.OptionalHeader.Magic );
return -ENOEXEC;
}
diff --git a/src/image/efi_siglist.c b/src/image/efi_siglist.c
new file mode 100644
index 000000000..71d597006
--- /dev/null
+++ b/src/image/efi_siglist.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2025 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.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * EFI signature lists
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/asn1.h>
+#include <ipxe/der.h>
+#include <ipxe/pem.h>
+#include <ipxe/image.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Guid/ImageAuthentication.h>
+#include <ipxe/efi/efi_siglist.h>
+
+/**
+ * Find EFI signature list entry
+ *
+ * @v data EFI signature list
+ * @v len Length of EFI signature list
+ * @v start Starting offset to update
+ * @v lhdr Signature list header to fill in
+ * @v dhdr Signature data header to fill in
+ * @ret rc Return status code
+ */
+static int efisig_find ( const void *data, size_t len, size_t *start,
+ const EFI_SIGNATURE_LIST **lhdr,
+ const EFI_SIGNATURE_DATA **dhdr ) {
+ size_t offset;
+ size_t remaining;
+ size_t skip;
+ size_t dlen;
+
+ /* Scan through signature list */
+ offset = 0;
+ while ( 1 ) {
+
+ /* Read list header */
+ assert ( offset <= len );
+ remaining = ( len - offset );
+ if ( remaining < sizeof ( **lhdr ) ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) truncated header "
+ "at +%#zx\n", *start, len, offset );
+ return -EINVAL;
+ }
+ *lhdr = ( data + offset );
+
+ /* Get length of this signature list */
+ if ( remaining < (*lhdr)->SignatureListSize ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) truncated list at "
+ "+%#zx\n", *start, len, offset );
+ return -EINVAL;
+ }
+ remaining = (*lhdr)->SignatureListSize;
+
+ /* Get length of each signature in list */
+ dlen = (*lhdr)->SignatureSize;
+ if ( dlen < sizeof ( **dhdr ) ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) underlength "
+ "signatures at +%#zx\n", *start, len, offset );
+ return -EINVAL;
+ }
+
+ /* Strip list header (including variable portion) */
+ if ( ( remaining < sizeof ( **lhdr ) ) ||
+ ( ( remaining - sizeof ( **lhdr ) ) <
+ (*lhdr)->SignatureHeaderSize ) ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) malformed header at "
+ "+%#zx\n", *start, len, offset );
+ return -EINVAL;
+ }
+ skip = ( sizeof ( **lhdr ) + (*lhdr)->SignatureHeaderSize );
+ offset += skip;
+ remaining -= skip;
+
+ /* Read signatures */
+ for ( ; remaining ; offset += dlen, remaining -= dlen ) {
+
+ /* Check length */
+ if ( remaining < dlen ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) truncated "
+ "at +%#zx\n", *start, len, offset );
+ return -EINVAL;
+ }
+
+ /* Continue until we find the requested signature */
+ if ( offset < *start )
+ continue;
+
+ /* Read data header */
+ *dhdr = ( data + offset );
+ DBGC2 ( data, "EFISIG [%#zx,%#zx) %s ",
+ offset, ( offset + dlen ),
+ efi_guid_ntoa ( &(*lhdr)->SignatureType ) );
+ DBGC2 ( data, "owner %s\n",
+ efi_guid_ntoa ( &(*dhdr)->SignatureOwner ) );
+ *start = offset;
+ return 0;
+ }
+ }
+}
+
+/**
+ * Extract ASN.1 object from EFI signature list
+ *
+ * @v data EFI signature list
+ * @v len Length of EFI signature list
+ * @v offset Offset within image
+ * @v cursor ASN.1 cursor to fill in
+ * @ret next Offset to next image, or negative error
+ *
+ * The caller is responsible for eventually calling free() on the
+ * allocated ASN.1 cursor.
+ */
+int efisig_asn1 ( const void *data, size_t len, size_t offset,
+ struct asn1_cursor **cursor ) {
+ const EFI_SIGNATURE_LIST *lhdr;
+ const EFI_SIGNATURE_DATA *dhdr;
+ int ( * asn1 ) ( const void *data, size_t len, size_t offset,
+ struct asn1_cursor **cursor );
+ size_t skip = offsetof ( typeof ( *dhdr ), SignatureData );
+ int next;
+ int rc;
+
+ /* Locate signature list entry */
+ if ( ( rc = efisig_find ( data, len, &offset, &lhdr, &dhdr ) ) != 0 )
+ goto err_entry;
+ len = ( offset + lhdr->SignatureSize );
+
+ /* Parse as PEM or DER based on first character */
+ asn1 = ( ( dhdr->SignatureData[0] == ASN1_SEQUENCE ) ?
+ der_asn1 : pem_asn1 );
+ DBGC2 ( data, "EFISIG [%#zx,%#zx) extracting %s\n", offset, len,
+ ( ( asn1 == der_asn1 ) ? "DER" : "PEM" ) );
+ next = asn1 ( data, len, ( offset + skip ), cursor );
+ if ( next < 0 ) {
+ rc = next;
+ DBGC ( data, "EFISIG [%#zx,%#zx) could not extract ASN.1: "
+ "%s\n", offset, len, strerror ( rc ) );
+ goto err_asn1;
+ }
+
+ /* Check that whole entry was consumed */
+ if ( ( ( unsigned int ) next ) != len ) {
+ DBGC ( data, "EFISIG [%#zx,%#zx) malformed data\n",
+ offset, len );
+ rc = -EINVAL;
+ goto err_whole;
+ }
+
+ return len;
+
+ err_whole:
+ free ( *cursor );
+ err_asn1:
+ err_entry:
+ return rc;
+}
+
+/**
+ * Probe EFI signature list image
+ *
+ * @v image EFI signature list
+ * @ret rc Return status code
+ */
+static int efisig_image_probe ( struct image *image ) {
+ const EFI_SIGNATURE_LIST *lhdr;
+ const EFI_SIGNATURE_DATA *dhdr;
+ size_t offset = 0;
+ unsigned int count = 0;
+ int rc;
+
+ /* Check file is a well-formed signature list */
+ while ( 1 ) {
+
+ /* Find next signature list entry */
+ if ( ( rc = efisig_find ( image->data, image->len, &offset,
+ &lhdr, &dhdr ) ) != 0 ) {
+ return rc;
+ }
+
+ /* Skip this entry */
+ offset += lhdr->SignatureSize;
+ count++;
+
+ /* Check if we have reached end of the image */
+ if ( offset == image->len ) {
+ DBGC ( image, "EFISIG %s contains %d signatures\n",
+ image->name, count );
+ return 0;
+ }
+ }
+}
+
+/**
+ * Extract ASN.1 object from EFI signature list image
+ *
+ * @v image EFI signature list
+ * @v offset Offset within image
+ * @v cursor ASN.1 cursor to fill in
+ * @ret next Offset to next image, or negative error
+ *
+ * The caller is responsible for eventually calling free() on the
+ * allocated ASN.1 cursor.
+ */
+static int efisig_image_asn1 ( struct image *image, size_t offset,
+ struct asn1_cursor **cursor ) {
+ int next;
+ int rc;
+
+ /* Extract ASN.1 object */
+ if ( ( next = efisig_asn1 ( image->data, image->len, offset,
+ cursor ) ) < 0 ) {
+ rc = next;
+ DBGC ( image, "EFISIG %s could not extract ASN.1: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return next;
+}
+
+/** EFI signature list image type */
+struct image_type efisig_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "EFISIG",
+ .probe = efisig_image_probe,
+ .asn1 = efisig_image_asn1,
+};
diff --git a/src/image/elf.c b/src/image/elf.c
index 5c2f9db25..8cbb610a6 100644
--- a/src/image/elf.c
+++ b/src/image/elf.c
@@ -33,11 +33,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* common ELF-related functionality.
*/
+#include <string.h>
#include <errno.h>
#include <elf.h>
-#include <ipxe/uaccess.h>
#include <ipxe/segment.h>
#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
#include <ipxe/elf.h>
/**
@@ -48,25 +49,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @v dest Destination address
* @ret rc Return status code
*/
-static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
+static int elf_load_segment ( struct image *image, const Elf_Phdr *phdr,
physaddr_t dest ) {
- userptr_t buffer = phys_to_user ( dest );
+ void *buffer = phys_to_virt ( dest );
int rc;
- DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n",
- image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
+ DBGC ( image, "ELF %s loading segment [%x,%x) to [%lx,%lx,%lx)\n",
+ image->name, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
dest, ( dest + phdr->p_filesz ), ( dest + 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 ) );
+ DBGC ( image, "ELF %s could not prepare segment: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
- memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
+ memcpy ( buffer, ( image->data + phdr->p_offset ), phdr->p_filesz );
return 0;
}
@@ -82,9 +83,11 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
* @ret max Maximum used address
* @ret rc Return status code
*/
-static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
+static int elf_segment ( struct image *image, const Elf_Ehdr *ehdr,
+ const Elf_Phdr *phdr,
int ( * process ) ( struct image *image,
- Elf_Phdr *phdr, physaddr_t dest ),
+ const Elf_Phdr *phdr,
+ physaddr_t dest ),
physaddr_t *entry, physaddr_t *max ) {
physaddr_t dest;
physaddr_t end;
@@ -97,7 +100,7 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
/* Check segment lies within image */
if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
- DBGC ( image, "ELF %p segment outside image\n", image );
+ DBGC ( image, "ELF %s segment outside image\n", image->name );
return -ENOEXEC;
}
@@ -109,8 +112,8 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
if ( ! dest )
dest = phdr->p_vaddr;
if ( ! dest ) {
- DBGC ( image, "ELF %p segment loads to physical address 0\n",
- image );
+ DBGC ( image, "ELF %s segment loads to physical address 0\n",
+ image->name );
return -ENOEXEC;
}
end = ( dest + phdr->p_memsz );
@@ -126,14 +129,14 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
/* 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 );
+ DBGC ( image, "ELF %s found physical entry point at %lx\n",
+ image->name, *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,
+ DBGC ( image, "ELF %s found virtual entry point at %lx"
+ " (virt %lx)\n", image->name, *entry,
( ( unsigned long ) ehdr->e_entry ) );
}
}
@@ -151,11 +154,12 @@ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr,
* @ret max Maximum used address
* @ret rc Return status code
*/
-int elf_segments ( struct image *image, Elf_Ehdr *ehdr,
- int ( * process ) ( struct image *image, Elf_Phdr *phdr,
+int elf_segments ( struct image *image, const Elf_Ehdr *ehdr,
+ int ( * process ) ( struct image *image,
+ const Elf_Phdr *phdr,
physaddr_t dest ),
physaddr_t *entry, physaddr_t *max ) {
- Elf_Phdr phdr;
+ const Elf_Phdr *phdr;
Elf_Off phoff;
unsigned int phnum;
int rc;
@@ -169,21 +173,22 @@ int elf_segments ( struct image *image, Elf_Ehdr *ehdr,
/* Read and process 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 );
+ if ( ( image->len < phoff ) ||
+ ( ( image->len - phoff ) < sizeof ( *phdr ) ) ) {
+ DBGC ( image, "ELF %s program header %d outside "
+ "image\n", image->name, phnum );
return -ENOEXEC;
}
- copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
- if ( ( rc = elf_segment ( image, ehdr, &phdr, process,
+ phdr = ( image->data + phoff );
+ if ( ( rc = elf_segment ( image, ehdr, phdr, process,
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 ) );
+ DBGC ( image, "ELF %s entry point %lx outside image\n",
+ image->name, ( ( unsigned long ) ehdr->e_entry ) );
return -ENOEXEC;
}
@@ -206,19 +211,23 @@ int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) {
[EI_MAG3] = ELFMAG3,
[EI_CLASS] = ELFCLASS,
};
- Elf_Ehdr ehdr;
+ const Elf_Ehdr *ehdr;
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 );
+ if ( image->len < sizeof ( *ehdr ) ) {
+ DBGC ( image, "ELF %s too short for ELF header\n",
+ image->name );
+ return -ENOEXEC;
+ }
+ ehdr = image->data;
+ if ( memcmp ( ehdr->e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
+ DBGC ( image, "ELF %s has invalid signature\n", image->name );
return -ENOEXEC;
}
/* Load ELF segments into memory */
- if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment,
+ if ( ( rc = elf_segments ( image, ehdr, elf_load_segment,
entry, max ) ) != 0 )
return rc;
diff --git a/src/image/embedded.c b/src/image/embedded.c
index 3c4bee655..22d3738cc 100644
--- a/src/image/embedded.c
+++ b/src/image/embedded.c
@@ -7,18 +7,18 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#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" \
+ extern size_t ABS_SYMBOL ( embedded_image_ ## _index ## _len ); \
+ __asm__ ( ".section \".data\", \"aw\", " PROGBITS "\n\t" \
"\nembedded_image_" #_index "_data:\n\t" \
".incbin \"" _path "\"\n\t" \
"\nembedded_image_" #_index "_end:\n\t" \
@@ -31,10 +31,11 @@ EMBED_ALL
/* Image structures for all embedded images */
#undef EMBED
#define EMBED( _index, _path, _name ) { \
- .refcnt = REF_INIT ( ref_no_free ), \
+ .refcnt = REF_INIT ( free_image ), \
.name = _name, \
- .data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \
- .len = ( size_t ) embedded_image_ ## _index ## _len, \
+ .flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
+ .rwdata = embedded_image_ ## _index ## _data, \
+ .len = ABS_VALUE_INIT ( embedded_image_ ## _index ## _len ), \
},
static struct image embedded_images[] = {
EMBED_ALL
@@ -57,18 +58,8 @@ static void embedded_init ( void ) {
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 ) );
@@ -90,5 +81,6 @@ static void embedded_init ( void ) {
/** Embedded image initialisation function */
struct init_fn embedded_init_fn __init_fn ( INIT_LATE ) = {
+ .name = "embedded",
.initialise = embedded_init,
};
diff --git a/src/image/gzip.c b/src/image/gzip.c
index 98376e113..17ccd2492 100644
--- a/src/image/gzip.c
+++ b/src/image/gzip.c
@@ -24,10 +24,10 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/deflate.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/zlib.h>
#include <ipxe/gzip.h>
@@ -46,85 +46,94 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret rc Return status code
*/
static int gzip_extract ( struct image *image, struct image *extracted ) {
- struct gzip_header header;
- struct gzip_extra_header extra;
- struct gzip_crc_header crc;
- struct gzip_footer footer;
- struct deflate_chunk in;
- unsigned int strings;
- size_t offset;
+ const struct gzip_header *header;
+ const struct gzip_extra_header *extra;
+ const struct gzip_crc_header *crc;
+ const struct gzip_footer *footer;
+ const void *data;
+ size_t extra_len;
+ size_t string_len;
size_t len;
- off_t nul;
+ unsigned int strings;
int rc;
/* Sanity check */
- assert ( image->len >= ( sizeof ( header ) + sizeof ( footer ) ) );
+ assert ( image->len >= ( sizeof ( *header ) + sizeof ( *footer ) ) );
+ data = image->data;
+ len = image->len;
/* Extract footer */
- len = ( image->len - sizeof ( footer ) );
- copy_from_user ( &footer, image->data, len, sizeof ( footer ) );
+ assert ( len >= sizeof ( *footer ) );
+ len -= sizeof ( *footer );
+ footer = ( data + len );
/* Extract fixed header */
- copy_from_user ( &header, image->data, 0, sizeof ( header ) );
- offset = sizeof ( header );
- assert ( offset <= ( image->len - sizeof ( footer ) ) );
+ assert ( len >= sizeof ( *header ) );
+ header = data;
+ data += sizeof ( *header );
+ len -= sizeof ( *header );
/* Skip extra header, if present */
- if ( header.flags & GZIP_FL_EXTRA ) {
- copy_from_user ( &extra, image->data, offset,
- sizeof ( extra ) );
- offset += sizeof ( extra );
- offset += le16_to_cpu ( extra.len );
- if ( offset > len ) {
- DBGC ( image, "GZIP %p overlength extra header\n",
- image );
+ if ( header->flags & GZIP_FL_EXTRA ) {
+ if ( len < sizeof ( *extra ) ) {
+ DBGC ( image, "GZIP %s overlength extra header\n",
+ image->name );
return -EINVAL;
}
+ extra = data;
+ data += sizeof ( *extra );
+ len -= sizeof ( *extra );
+ extra_len = le16_to_cpu ( extra->len );
+ if ( len < extra_len ) {
+ DBGC ( image, "GZIP %s overlength extra header\n",
+ image->name );
+ return -EINVAL;
+ }
+ data += extra_len;
+ len -= extra_len;
}
- assert ( offset <= ( image->len - sizeof ( footer ) ) );
/* Skip name and/or comment, if present */
strings = 0;
- if ( header.flags & GZIP_FL_NAME )
+ if ( header->flags & GZIP_FL_NAME )
strings++;
- if ( header.flags & GZIP_FL_COMMENT )
+ if ( header->flags & GZIP_FL_COMMENT )
strings++;
while ( strings-- ) {
- nul = memchr_user ( image->data, offset, 0, ( len - offset ) );
- if ( nul < 0 ) {
- DBGC ( image, "GZIP %p overlength name/comment\n",
- image );
+ string_len = strnlen ( data, len );
+ if ( string_len == len ) {
+ DBGC ( image, "GZIP %s overlength name/comment\n",
+ image->name );
return -EINVAL;
}
- offset = ( nul + 1 /* NUL */ );
+ data += ( string_len + 1 /* NUL */ );
+ len -= ( string_len + 1 /* NUL */ );
}
- assert ( offset <= ( image->len - sizeof ( footer ) ) );
/* Skip CRC, if present */
- if ( header.flags & GZIP_FL_HCRC ) {
- offset += sizeof ( crc );
- if ( offset > len ) {
- DBGC ( image, "GZIP %p overlength CRC header\n",
- image );
+ if ( header->flags & GZIP_FL_HCRC ) {
+ if ( len < sizeof ( *crc ) ) {
+ DBGC ( image, "GZIP %s overlength CRC header\n",
+ image->name );
return -EINVAL;
}
+ data += sizeof ( *crc );
+ len -= sizeof ( *crc );
}
- /* Initialise input chunk */
- deflate_chunk_init ( &in, userptr_add ( image->data, offset ), 0, len );
-
/* Presize extracted image */
if ( ( rc = image_set_len ( extracted,
- le32_to_cpu ( footer.len ) ) ) != 0 ) {
- DBGC ( image, "GZIP %p could not presize: %s\n",
- image, strerror ( rc ) );
+ le32_to_cpu ( footer->len ) ) ) != 0 ) {
+ DBGC ( image, "GZIP %s could not presize: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
/* Decompress image (expanding if necessary) */
- if ( ( rc = zlib_deflate ( DEFLATE_RAW, &in, extracted ) ) != 0 ) {
- DBGC ( image, "GZIP %p could not decompress: %s\n",
- image, strerror ( rc ) );
+ if ( ( rc = zlib_deflate ( DEFLATE_RAW, data, len,
+ extracted ) ) != 0 ) {
+ DBGC ( image, "GZIP %s could not decompress: %s\n",
+ image->name, strerror ( rc ) );
return rc;
}
@@ -138,20 +147,19 @@ static int gzip_extract ( struct image *image, struct image *extracted ) {
* @ret rc Return status code
*/
static int gzip_probe ( struct image *image ) {
- struct gzip_header header;
- struct gzip_footer footer;
+ const struct gzip_header *header;
+ const struct gzip_footer *footer;
/* Sanity check */
- if ( image->len < ( sizeof ( header ) + sizeof ( footer ) ) ) {
- DBGC ( image, "GZIP %p image too short\n", image );
+ if ( image->len < ( sizeof ( *header ) + sizeof ( *footer ) ) ) {
+ DBGC ( image, "GZIP %s image too short\n", image->name );
return -ENOEXEC;
}
+ header = image->data;
/* Check magic header */
- copy_from_user ( &header.magic, image->data, 0,
- sizeof ( header.magic ) );
- if ( header.magic != cpu_to_be16 ( GZIP_MAGIC ) ) {
- DBGC ( image, "GZIP %p invalid magic\n", image );
+ if ( header->magic != cpu_to_be16 ( GZIP_MAGIC ) ) {
+ DBGC ( image, "GZIP %s invalid magic\n", image->name );
return -ENOEXEC;
}
diff --git a/src/image/initrd.c b/src/image/initrd.c
new file mode 100644
index 000000000..ba550a8f2
--- /dev/null
+++ b/src/image/initrd.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2012 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 (at your option) 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.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/init.h>
+#include <ipxe/cpio.h>
+#include <ipxe/uheap.h>
+#include <ipxe/initrd.h>
+
+/** @file
+ *
+ * Initial ramdisk (initrd) reshuffling
+ *
+ */
+
+/** End of reshuffle region */
+static physaddr_t initrd_end;
+
+/**
+ * Squash initrds as high as possible in memory
+ *
+ * @v start Start of reshuffle region
+ * @v end End of reshuffle region
+ */
+static void initrd_squash_high ( physaddr_t start, physaddr_t end ) {
+ physaddr_t current = end;
+ struct image *initrd;
+ struct image *highest;
+ void *data;
+
+ /* Squash up any initrds already within the region */
+ while ( 1 ) {
+
+ /* Find the highest image not yet in its final position */
+ highest = NULL;
+ for_each_image ( initrd ) {
+ if ( ( virt_to_phys ( initrd->data ) >= start ) &&
+ ( virt_to_phys ( initrd->data ) < current ) &&
+ ( ( highest == NULL ) ||
+ ( virt_to_phys ( initrd->data ) >
+ virt_to_phys ( highest->data ) ) ) ) {
+ highest = initrd;
+ }
+ }
+ if ( ! highest )
+ break;
+
+ /* Calculate final position */
+ current -= initrd_align ( highest->len );
+ if ( current <= virt_to_phys ( highest->data ) ) {
+ /* Already at (or crossing) end of region */
+ current = virt_to_phys ( highest->data );
+ continue;
+ }
+
+ /* Move this image to its final position */
+ DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
+ "[%#08lx,%#08lx)\n", highest->name,
+ virt_to_phys ( highest->data ),
+ ( virt_to_phys ( highest->data ) + highest->len ),
+ current, ( current + highest->len ) );
+ data = phys_to_virt ( current );
+ memmove ( data, highest->data, highest->len );
+ highest->data = data;
+ }
+}
+
+/**
+ * Reverse aligned memory region
+ *
+ * @v data Memory region
+ * @v len Length of region
+ */
+static void initrd_reverse ( void *data, size_t len ) {
+ unsigned long *low = data;
+ unsigned long *high = ( data + len );
+ unsigned long tmp;
+
+ /* Reverse region */
+ for ( high-- ; low < high ; low++, high-- ) {
+ tmp = *low;
+ *low = *high;
+ *high = tmp;
+ }
+}
+
+/**
+ * Swap position of two adjacent initrds
+ *
+ * @v low Lower initrd
+ * @v high Higher initrd
+ */
+static void initrd_swap ( struct image *low, struct image *high ) {
+ size_t low_len;
+ size_t high_len;
+ size_t len;
+ void *data;
+
+ DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
+ "%s\n", low->name, virt_to_phys ( low->data ),
+ ( virt_to_phys ( low->data ) + low->len ),
+ virt_to_phys ( high->data ),
+ ( virt_to_phys ( high->data ) + high->len ), high->name );
+
+ /* Calculate padded lengths */
+ low_len = initrd_align ( low->len );
+ high_len = initrd_align ( high->len );
+ len = ( low_len + high_len );
+ data = low->rwdata;
+ assert ( high->data == ( data + low_len ) );
+
+ /* Adjust data pointers */
+ high->data -= low_len;
+ low->data += high_len;
+ assert ( high->data == data );
+
+ /* Swap content via triple reversal */
+ initrd_reverse ( data, len );
+ initrd_reverse ( low->rwdata, low_len );
+ initrd_reverse ( high->rwdata, high_len );
+}
+
+/**
+ * Swap position of any two adjacent initrds not currently in the correct order
+ *
+ * @v start Start of reshuffle region
+ * @v end End of reshuffle region
+ * @ret swapped A pair of initrds was swapped
+ */
+static int initrd_swap_any ( physaddr_t start, physaddr_t end ) {
+ struct image *low;
+ struct image *high;
+ const void *adjacent;
+ physaddr_t addr;
+
+ /* Find any pair of initrds that can be swapped */
+ for_each_image ( low ) {
+
+ /* Ignore images wholly outside the reshuffle region */
+ addr = virt_to_phys ( low->data );
+ if ( ( addr < start ) || ( addr >= end ) )
+ continue;
+
+ /* Calculate location of adjacent image (if any) */
+ adjacent = ( low->data + initrd_align ( low->len ) );
+
+ /* Search for adjacent image */
+ for_each_image ( high ) {
+
+ /* Ignore images wholly outside the reshuffle region */
+ addr = virt_to_phys ( high->data );
+ if ( ( addr < start ) || ( addr >= end ) )
+ continue;
+
+ /* Stop search if all remaining potential
+ * adjacent images are already in the correct
+ * order.
+ */
+ if ( high == low )
+ break;
+
+ /* If we have found the adjacent image, swap and exit */
+ if ( high->data == adjacent ) {
+ initrd_swap ( low, high );
+ return 1;
+ }
+ }
+ }
+
+ /* Nothing swapped */
+ return 0;
+}
+
+/**
+ * Dump initrd locations (for debug)
+ *
+ */
+static void initrd_dump ( void ) {
+ struct image *initrd;
+
+ /* Do nothing unless debugging is enabled */
+ if ( ! DBG_LOG )
+ return;
+
+ /* Dump initrd locations */
+ for_each_image ( initrd ) {
+ DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
+ initrd->name, virt_to_phys ( initrd->data ),
+ ( virt_to_phys ( initrd->data ) + initrd->len ) );
+ DBGC2_MD5A ( &images, virt_to_phys ( initrd->data ),
+ initrd->data, initrd->len );
+ }
+}
+
+/**
+ * Reshuffle initrds into desired order at top of memory
+ *
+ * After this function returns, the initrds have been rearranged in
+ * memory and the external heap structures will have been corrupted.
+ * Reshuffling must therefore take place immediately prior to jumping
+ * to the loaded OS kernel; no further execution within iPXE is
+ * permitted.
+ */
+void initrd_reshuffle ( void ) {
+ physaddr_t start;
+ physaddr_t end;
+
+ /* Calculate limits of reshuffle region */
+ start = uheap_limit;
+ end = ( initrd_end ? initrd_end : uheap_end );
+
+ /* Debug */
+ initrd_dump();
+
+ /* Squash initrds as high as possible in memory */
+ initrd_squash_high ( start, end );
+
+ /* Bubble-sort initrds into desired order */
+ while ( initrd_swap_any ( start, end ) ) {}
+
+ /* Debug */
+ initrd_dump();
+}
+
+/**
+ * Load initrd
+ *
+ * @v initrd initrd image
+ * @v address Address at which to load, or NULL
+ * @ret len Length of loaded image, excluding zero-padding
+ */
+static size_t initrd_load ( struct image *initrd, void *address ) {
+ const char *filename = cpio_name ( initrd );
+ struct cpio_header cpio;
+ size_t offset;
+ size_t cpio_len;
+ size_t len;
+ unsigned int i;
+
+ /* Sanity check */
+ assert ( ( address == NULL ) ||
+ ( ( virt_to_phys ( address ) & ( INITRD_ALIGN - 1 ) ) == 0 ));
+
+ /* Skip hidden images */
+ if ( initrd->flags & IMAGE_HIDDEN )
+ return 0;
+
+ /* Determine length of cpio headers for non-prebuilt images */
+ len = 0;
+ for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ )
+ len += ( cpio_len + cpio_pad_len ( cpio_len ) );
+
+ /* Copy in initrd image body and construct any cpio headers */
+ if ( address ) {
+ memmove ( ( address + len ), initrd->data, initrd->len );
+ memset ( address, 0, len );
+ offset = 0;
+ for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ;
+ i++ ) {
+ memcpy ( ( address + offset ), &cpio,
+ sizeof ( cpio ) );
+ memcpy ( ( address + offset + sizeof ( cpio ) ),
+ filename, ( cpio_len - sizeof ( cpio ) ) );
+ offset += ( cpio_len + cpio_pad_len ( cpio_len ) );
+ }
+ assert ( offset == len );
+ DBGC ( &images, "INITRD %s [%#08lx,%#08lx,%#08lx)%s%s\n",
+ initrd->name, virt_to_phys ( address ),
+ ( virt_to_phys ( address ) + offset ),
+ ( virt_to_phys ( address ) + offset + initrd->len ),
+ ( filename ? " " : "" ), ( filename ? filename : "" ) );
+ DBGC2_MD5A ( &images, ( virt_to_phys ( address ) + offset ),
+ ( address + offset ), initrd->len );
+ }
+ len += initrd->len;
+
+ return len;
+}
+
+/**
+ * Load all initrds
+ *
+ * @v address Load address, or NULL
+ * @ret len Length
+ *
+ * This function is called after the point of no return, when the
+ * external heap has been corrupted by reshuffling and there is no way
+ * to resume normal execution. The caller must have previously
+ * ensured that there is no way for installation to this address to
+ * fail.
+ */
+size_t initrd_load_all ( void *address ) {
+ struct image *initrd;
+ size_t len = 0;
+ size_t pad_len;
+ void *dest;
+
+ /* Load all initrds */
+ for_each_image ( initrd ) {
+
+ /* Zero-pad to next INITRD_ALIGN boundary */
+ pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) );
+ if ( address )
+ memset ( ( address + len ), 0, pad_len );
+ len += pad_len;
+ assert ( len == initrd_align ( len ) );
+
+ /* Load initrd */
+ dest = ( address ? ( address + len ) : NULL );
+ len += initrd_load ( initrd, dest );
+ }
+
+ return len;
+}
+
+/**
+ * Calculate post-reshuffle initrd load region
+ *
+ * @v len Length of initrds (from initrd_len())
+ * @v region Region descriptor to fill in
+ * @ret rc Return status code
+ *
+ * If successful, then any suitably aligned range within the region
+ * may be used as the load address after reshuffling. The caller does
+ * not need to call prep_segment() for a range in this region.
+ * (Calling prep_segment() would probably fail, since prior to
+ * reshuffling the region is still in use by the external heap.)
+ */
+int initrd_region ( size_t len, struct memmap_region *region ) {
+ physaddr_t min;
+ size_t available;
+
+ /* Calculate limits of available space for initrds */
+ min = uheap_limit;
+ available = ( ( initrd_end ? initrd_end : uheap_end ) - min );
+ if ( available < len )
+ return -ENOSPC;
+ DBGC ( &images, "INITRD post-reshuffle region is [%#08lx,%#08lx)\n",
+ min, ( min + available ) );
+
+ /* Populate region descriptor */
+ region->min = min;
+ region->max = ( min + available - 1 );
+ region->flags = MEMMAP_FL_MEMORY;
+ region->name = "initrd";
+
+ return 0;
+}
+
+/**
+ * initrd startup function
+ *
+ */
+static void initrd_startup ( void ) {
+
+ /* Record address above which reshuffling cannot take place.
+ * If any external heap allocations have been made during
+ * driver startup (e.g. large host memory blocks for
+ * Infiniband devices, which may still be in use at the time
+ * of rearranging if a SAN device is hooked), then we must not
+ * overwrite these allocations during reshuffling.
+ */
+ initrd_end = uheap_start;
+ if ( initrd_end ) {
+ DBGC ( &images, "INITRD limiting reshuffling to below "
+ "%#08lx\n", initrd_end );
+ }
+}
+
+/** initrd startup function */
+struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
+ .name = "initrd",
+ .startup = initrd_startup,
+};
diff --git a/src/image/lkrn.c b/src/image/lkrn.c
new file mode 100644
index 000000000..a2044cb82
--- /dev/null
+++ b/src/image/lkrn.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2025 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 (at your option) 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.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/image.h>
+#include <ipxe/memmap.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/segment.h>
+#include <ipxe/initrd.h>
+#include <ipxe/io.h>
+#include <ipxe/fdt.h>
+#include <ipxe/init.h>
+#include <ipxe/lkrn.h>
+
+/** @file
+ *
+ * Linux kernel image format
+ *
+ */
+
+/**
+ * Parse kernel image
+ *
+ * @v image Kernel image
+ * @v ctx Kernel image context
+ * @ret rc Return status code
+ */
+static int lkrn_parse ( struct image *image, struct lkrn_context *ctx ) {
+ const struct lkrn_header *hdr;
+
+ /* Initialise context */
+ memset ( ctx, 0, sizeof ( *ctx ) );
+
+ /* Read image header */
+ if ( image->len < sizeof ( *hdr ) ) {
+ DBGC ( image, "LKRN %s too short for header\n", image->name );
+ return -ENOEXEC;
+ }
+ hdr = image->data;
+
+ /* Check magic value */
+ if ( hdr->magic != cpu_to_le32 ( LKRN_MAGIC_ARCH ) ) {
+ DBGC ( image, "LKRN %s bad magic value %#08x\n",
+ image->name, le32_to_cpu ( hdr->magic ) );
+ return -ENOEXEC;
+ }
+
+ /* Record load offset */
+ ctx->offset = le64_to_cpu ( hdr->text_offset );
+ if ( ctx->offset & ( ctx->offset - 1 ) ) {
+ DBGC ( image, "LKRN %s offset %#zx is not a power of two\n",
+ image->name, ctx->offset );
+ return -ENOEXEC;
+ }
+
+ /* Record and check image size */
+ ctx->filesz = image->len;
+ ctx->memsz = le64_to_cpu ( hdr->image_size );
+ if ( ctx->filesz > ctx->memsz ) {
+ DBGC ( image, "LKRN %s invalid image size %#zx/%#zx\n",
+ image->name, ctx->filesz, ctx->memsz );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Locate start of RAM
+ *
+ * @v image Kernel image
+ * @v ctx Kernel image context
+ * @ret rc Return status code
+ */
+static int lkrn_ram ( struct image *image, struct lkrn_context *ctx ) {
+ struct memmap_region region;
+
+ /* Locate start of RAM */
+ for_each_memmap ( &region, 0 ) {
+ DBGC_MEMMAP ( image, &region );
+ if ( ! ( region.flags & MEMMAP_FL_MEMORY ) )
+ continue;
+ ctx->ram = region.min;
+ DBGC ( image, "LKRN %s RAM starts at %#08lx\n",
+ image->name, ctx->ram );
+ return 0;
+ }
+
+ DBGC ( image, "LKRN %s found no RAM\n", image->name );
+ return -ENOTSUP;
+}
+
+/**
+ * Execute kernel image
+ *
+ * @v image Kernel image
+ * @ret rc Return status code
+ */
+static int lkrn_exec ( struct image *image ) {
+ static struct image fdtimg = {
+ .refcnt = REF_INIT ( free_image ),
+ .name = "<FDT>",
+ .flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ),
+ };
+ struct lkrn_context ctx;
+ struct memmap_region region;
+ struct fdt_header *fdt;
+ size_t initrdsz;
+ size_t totalsz;
+ void *dest;
+ int rc;
+
+ /* Parse header */
+ if ( ( rc = lkrn_parse ( image, &ctx ) ) != 0 )
+ goto err_parse;
+
+ /* Locate start of RAM */
+ if ( ( rc = lkrn_ram ( image, &ctx ) ) != 0 )
+ goto err_ram;
+
+ /* Place kernel at specified address from start of RAM */
+ ctx.entry = ( ctx.ram + ctx.offset );
+ DBGC ( image, "LKRN %s loading to [%#08lx,%#08lx,%#08lx)\n",
+ image->name, ctx.entry, ( ctx.entry + ctx.filesz ),
+ ( ctx.entry + ctx.memsz ) );
+
+ /* Place initrd after kernel, aligned to the kernel's image offset */
+ ctx.initrd = ( ctx.ram + initrd_align ( ctx.offset + ctx.memsz ) );
+ ctx.initrd = ( ( ctx.initrd + ctx.offset - 1 ) & ~( ctx.offset - 1 ) );
+ initrdsz = initrd_len();
+ if ( initrdsz ) {
+ DBGC ( image, "LKRN %s initrd at [%#08lx,%#08lx)\n",
+ image->name, ctx.initrd, ( ctx.initrd + initrdsz ) );
+ }
+
+ /* Place device tree after initrd */
+ ctx.fdt = ( ctx.initrd + initrd_align ( initrdsz ) );
+
+ /* Construct device tree and post-initrd image */
+ if ( ( rc = fdt_create ( &fdt, image->cmdline, ctx.initrd,
+ initrdsz ) ) != 0 ) {
+ goto err_fdt;
+ }
+ fdtimg.data = fdt;
+ fdtimg.len = be32_to_cpu ( fdt->totalsize );
+ list_add_tail ( &fdtimg.list, &images );
+ DBGC ( image, "LKRN %s FDT at [%08lx,%08lx)\n",
+ image->name, ctx.fdt, ( ctx.fdt + fdtimg.len ) );
+
+ /* Find post-reshuffle region */
+ if ( ( rc = initrd_region ( initrdsz, &region ) ) != 0 ) {
+ DBGC ( image, "LKRN %s no available region: %s\n",
+ image->name, strerror ( rc ) );
+ goto err_region;
+ }
+
+ /* Check that everything can be placed at its target addresses */
+ totalsz = ( ctx.fdt + fdtimg.len - ctx.ram );
+ if ( ( ctx.entry >= region.min ) &&
+ ( ( ctx.offset + totalsz ) <= memmap_size ( &region ) ) ) {
+ /* Target addresses are within the reshuffle region */
+ DBGC ( image, "LKRN %s fits within reshuffle region\n",
+ image->name );
+ } else {
+ /* Target addresses are outside the reshuffle region */
+ if ( ( rc = prep_segment ( phys_to_virt ( ctx.entry ),
+ totalsz, totalsz ) ) != 0 ) {
+ DBGC ( image, "LKRN %s could not prepare segment: "
+ "%s\n", image->name, strerror ( rc ) );
+ goto err_segment;
+ }
+ }
+
+ /* This is the point of no return: we are about to reshuffle
+ * and thereby destroy the external heap. No errors are
+ * allowed to occur after this point.
+ */
+
+ /* Shut down ready for boot */
+ shutdown_boot();
+
+ /* Prepend kernel to reshuffle list, reshuffle, and remove kernel */
+ list_add ( &image->list, &images );
+ initrd_reshuffle();
+ list_del ( &image->list );
+
+ /* Load kernel to entry point and zero bss */
+ dest = phys_to_virt ( ctx.entry );
+ memmove ( dest, image->data, ctx.filesz );
+ memset ( ( dest + ctx.filesz ), 0, ( ctx.memsz - ctx.filesz ) );
+
+ /* Load initrds and device tree */
+ dest = phys_to_virt ( ctx.initrd );
+ initrd_load_all ( dest );
+
+ /* Jump to kernel entry point */
+ DBGC ( image, "LKRN %s jumping to kernel at %#08lx\n",
+ image->name, ctx.entry );
+ lkrn_jump ( ctx.entry, ctx.fdt );
+
+ /* There is no way for the image to return, since we provide
+ * no return address.
+ */
+ assert ( 0 );
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+
+ err_segment:
+ err_region:
+ list_del ( &fdtimg.list );
+ fdt_remove ( fdt );
+ err_fdt:
+ err_ram:
+ err_parse:
+ return rc;
+}
+
+/**
+ * Probe kernel image
+ *
+ * @v image Kernel image
+ * @ret rc Return status code
+ */
+static int lkrn_probe ( struct image *image ) {
+ struct lkrn_context ctx;
+ int rc;
+
+ /* Parse header */
+ if ( ( rc = lkrn_parse ( image, &ctx ) ) != 0 )
+ return rc;
+
+ DBGC ( image, "LKRN %s is a Linux kernel\n", image->name );
+ return 0;
+}
+
+/** Linux kernel image type */
+struct image_type lkrn_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "lkrn",
+ .probe = lkrn_probe,
+ .exec = lkrn_exec,
+};
+
+/**
+ * Parse compressed kernel image
+ *
+ * @v image Compressed kernel image
+ * @v zctx Compressed kernel image context
+ * @ret rc Return status code
+ */
+static int zimg_parse ( struct image *image, struct zimg_context *zctx ) {
+ const struct zimg_header *zhdr;
+
+ /* Initialise context */
+ memset ( zctx, 0, sizeof ( *zctx ) );
+
+ /* Parse header */
+ if ( image->len < sizeof ( *zhdr ) ) {
+ DBGC ( image, "ZIMG %s too short for header\n",
+ image->name );
+ return -ENOEXEC;
+ }
+ zhdr = image->data;
+
+ /* Check magic value */
+ if ( zhdr->magic != cpu_to_le32 ( ZIMG_MAGIC ) ) {
+ DBGC ( image, "ZIMG %s bad magic value %#08x\n",
+ image->name, le32_to_cpu ( zhdr->magic ) );
+ return -ENOEXEC;
+ }
+
+ /* Record and check offset and length */
+ zctx->offset = le32_to_cpu ( zhdr->offset );
+ zctx->len = le32_to_cpu ( zhdr->len );
+ if ( ( zctx->offset > image->len ) ||
+ ( zctx->len > ( image->len - zctx->offset ) ) ) {
+ DBGC ( image, "ZIMG %s bad range [+%#zx,+%#zx)/%#zx\n",
+ image->name, zctx->offset,
+ (zctx->offset + zctx->len ), image->len );
+ return -ENOEXEC;
+ }
+
+ /* Record compression type */
+ zctx->type.raw = zhdr->type;
+
+ return 0;
+}
+
+/**
+ * Extract compresed kernel image
+ *
+ * @v image Compressed kernel image
+ * @v extracted Extracted image
+ * @ret rc Return status code
+ */
+static int zimg_extract ( struct image *image, struct image *extracted ) {
+ struct zimg_context zctx;
+ const void *payload;
+ int rc;
+
+ /* Parse header */
+ if ( ( rc = zimg_parse ( image, &zctx ) ) != 0 )
+ return rc;
+ DBGC ( image, "ZIMG %s has %s-compressed payload at [+%#zx,+%#zx)\n",
+ image->name, zctx.type.string, zctx.offset,
+ ( zctx.offset + zctx.len ) );
+
+ /* Extract compressed payload */
+ payload = ( image->data + zctx.offset );
+ if ( ( rc = image_set_data ( extracted, payload, zctx.len ) ) != 0 ) {
+ DBGC ( image, "ZIMG %s could not extract: %s\n",
+ image->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Probe compressed kernel image
+ *
+ * @v image Compressed kernel image
+ * @ret rc Return status code
+ */
+static int zimg_probe ( struct image *image ) {
+ struct zimg_context zctx;
+ int rc;
+
+ /* Parse header */
+ if ( ( rc = zimg_parse ( image, &zctx ) ) != 0 )
+ return rc;
+
+ DBGC ( image, "ZIMG %s is a %s-compressed Linux kernel\n",
+ image->name, zctx.type.string );
+ return 0;
+}
+
+/** Linux kernel compressed image type */
+struct image_type zimg_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "zimg",
+ .probe = zimg_probe,
+ .extract = zimg_extract,
+ .exec = image_extract_exec,
+};
diff --git a/src/image/pem.c b/src/image/pem.c
index 2dcc36442..0fea5fbea 100644
--- a/src/image/pem.c
+++ b/src/image/pem.c
@@ -22,13 +22,13 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/asn1.h>
#include <ipxe/base64.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/pem.h>
@@ -46,14 +46,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @v offset Starting offset
* @ret next Offset to next line
*/
-static size_t pem_next ( userptr_t data, size_t len, size_t offset ) {
- off_t eol;
+static size_t pem_next ( const void *data, size_t len, size_t offset ) {
+ const void *sep;
/* Find and skip next newline character, if any */
- eol = memchr_user ( data, offset, '\n', ( len - offset ) );
- if ( eol < 0 )
+ sep = memchr ( ( data + offset ), '\n', ( len - offset ) );
+ if ( ! sep )
return len;
- return ( eol + 1 );
+ return ( ( sep - data ) + 1 );
}
/**
@@ -65,9 +65,9 @@ static size_t pem_next ( userptr_t data, size_t len, size_t offset ) {
* @v marker Boundary marker
* @ret offset Offset to boundary marker line, or negative error
*/
-static int pem_marker ( userptr_t data, size_t len, size_t offset,
+static int pem_marker ( const void *data, size_t len, size_t offset,
const char *marker ) {
- char buf[ strlen ( marker ) ];
+ size_t marker_len = strlen ( marker );
/* Sanity check */
assert ( offset <= len );
@@ -76,10 +76,9 @@ static int pem_marker ( userptr_t data, size_t len, size_t offset,
while ( offset < len ) {
/* Check for marker */
- if ( ( len - offset ) < sizeof ( buf ) )
+ if ( ( len - offset ) < marker_len )
break;
- copy_from_user ( buf, data, offset, sizeof ( buf ) );
- if ( memcmp ( buf, marker, sizeof ( buf ) ) == 0 )
+ if ( memcmp ( ( data + offset ), marker, marker_len ) == 0 )
return offset;
/* Move to next line */
@@ -102,7 +101,7 @@ static int pem_marker ( userptr_t data, size_t len, size_t offset,
* The caller is responsible for eventually calling free() on the
* allocated ASN.1 cursor.
*/
-int pem_asn1 ( userptr_t data, size_t len, size_t offset,
+int pem_asn1 ( const void *data, size_t len, size_t offset,
struct asn1_cursor **cursor ) {
size_t encoded_len;
size_t decoded_max_len;
@@ -140,7 +139,7 @@ int pem_asn1 ( userptr_t data, size_t len, size_t offset,
rc = -ENOMEM;
goto err_alloc_encoded;
}
- copy_from_user ( encoded, data, begin, encoded_len );
+ memcpy ( encoded, ( data + begin ), encoded_len );
encoded[encoded_len] = '\0';
/* Allocate cursor and data buffer */
diff --git a/src/image/png.c b/src/image/png.c
index d5cf7fd8f..ab279eae5 100644
--- a/src/image/png.c
+++ b/src/image/png.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -197,12 +198,12 @@ static size_t png_scanline_len ( struct png_context *png,
*/
static int png_image_header ( struct image *image, struct png_context *png,
size_t len ) {
- struct png_image_header ihdr;
+ const struct png_image_header *ihdr;
struct png_interlace interlace;
unsigned int pass;
/* Sanity check */
- if ( len != sizeof ( ihdr ) ) {
+ if ( len != sizeof ( *ihdr ) ) {
DBGC ( image, "PNG %s invalid IHDR length %zd\n",
image->name, len );
return -EINVAL;
@@ -213,32 +214,32 @@ static int png_image_header ( struct image *image, struct png_context *png,
}
/* Extract image header */
- copy_from_user ( &ihdr, image->data, png->offset, len );
+ ihdr = ( image->data + png->offset );
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 );
+ "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 ) {
+ if ( ihdr->compression >= PNG_COMPRESSION_UNKNOWN ) {
DBGC ( image, "PNG %s unknown compression method %d\n",
- image->name, ihdr.compression );
+ image->name, ihdr->compression );
return -ENOTSUP;
}
- if ( ihdr.filter >= PNG_FILTER_UNKNOWN ) {
+ if ( ihdr->filter >= PNG_FILTER_UNKNOWN ) {
DBGC ( image, "PNG %s unknown filter method %d\n",
- image->name, ihdr.filter );
+ image->name, ihdr->filter );
return -ENOTSUP;
}
- if ( ihdr.interlace >= PNG_INTERLACE_UNKNOWN ) {
+ if ( ihdr->interlace >= PNG_INTERLACE_UNKNOWN ) {
DBGC ( image, "PNG %s unknown interlace method %d\n",
- image->name, ihdr.interlace );
+ image->name, ihdr->interlace );
return -ENOTSUP;
}
/* Allocate pixel buffer */
- png->pixbuf = alloc_pixbuf ( ntohl ( ihdr.width ),
- ntohl ( ihdr.height ) );
+ 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 );
@@ -246,7 +247,7 @@ static int png_image_header ( struct image *image, struct png_context *png,
}
/* Extract bit depth */
- png->depth = ihdr.depth;
+ png->depth = ihdr->depth;
if ( ( png->depth == 0 ) ||
( ( png->depth & ( png->depth - 1 ) ) != 0 ) ) {
DBGC ( image, "PNG %s invalid depth %d\n",
@@ -255,17 +256,17 @@ static int png_image_header ( struct image *image, struct png_context *png,
}
/* Calculate number of channels */
- png->colour_type = ihdr.colour_type;
+ 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 )
+ 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 )
+ if ( ihdr->colour_type & PNG_COLOUR_TYPE_ALPHA )
png->channels += 1;
}
/* Calculate number of interlace passes */
- png->passes = png_interlace_passes[ihdr.interlace];
+ png->passes = png_interlace_passes[ihdr->interlace];
/* Calculate length of raw data buffer */
for ( pass = 0 ; pass < png->passes ; pass++ ) {
@@ -297,30 +298,28 @@ static int png_image_header ( struct image *image, struct png_context *png,
*/
static int png_palette ( struct image *image, struct png_context *png,
size_t len ) {
- size_t offset = png->offset;
- struct png_palette_entry palette;
+ const struct png_palette_entry *palette;
unsigned int i;
/* Populate palette */
+ palette = ( image->data + png->offset );
for ( i = 0 ; i < ( sizeof ( png->palette ) /
sizeof ( png->palette[0] ) ) ; i++ ) {
/* Stop when we run out of palette data */
- if ( len < sizeof ( palette ) )
+ 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 ) );
+ 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 );
+ palette++;
+ len -= sizeof ( *palette );
}
return 0;
@@ -336,13 +335,12 @@ static int png_palette ( struct image *image, struct png_context *png,
*/
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 ) {
+ if ( ( rc = deflate_inflate ( &png->deflate,
+ ( image->data + png->offset ),
+ len, &png->raw ) ) != 0 ) {
DBGC ( image, "PNG %s could not decompress: %s\n",
image->name, strerror ( rc ) );
return rc;
@@ -504,17 +502,16 @@ static struct png_filter png_filters[] = {
*/
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 );
+ uint8_t *data = ( png->raw.data + png->raw.offset );
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;
+ unsigned int filter_type;
+ unsigned int left;
+ unsigned int above;
+ unsigned int above_left;
/* On the first scanline of a pass, above bytes are assumed to
* be zero.
@@ -525,8 +522,7 @@ static int png_unfilter_pass ( struct image *image, struct png_context *png,
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 ) );
+ filter_type = *(data++);
if ( filter_type >= ( sizeof ( png_filters ) /
sizeof ( png_filters[0] ) ) ) {
DBGC ( image, "PNG %s unknown filter type %d\n",
@@ -548,35 +544,24 @@ static int png_unfilter_pass ( struct image *image, struct png_context *png,
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 ( byte >= pixel_len )
+ left = *( data - pixel_len );
+ if ( scanline > 0 )
+ above = *( data - scanline_len );
if ( ( scanline > 0 ) && ( byte >= pixel_len ) ) {
- copy_from_user ( &above_left, png->raw.data,
- ( offset - scanline_len -
- pixel_len ),
- sizeof ( above_left ) );
+ above_left = *( data - scanline_len -
+ pixel_len );
}
/* 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 ) );
+ *data = filter->unfilter ( *data, left, above,
+ above_left );
+ data++;
}
}
/* Update offset */
- png->raw.offset = offset;
+ png->raw.offset = ( ( ( void * ) data ) - png->raw.data );
return 0;
}
@@ -651,16 +636,16 @@ static inline unsigned int png_pixel ( unsigned int raw, unsigned int alpha,
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;
+ const uint8_t *data = ( png->raw.data + png->raw.offset );
+ size_t data_stride;
+ unsigned int pixbuf_y_index;
+ unsigned int pixbuf_index;
+ unsigned int pixbuf_x_stride;
+ unsigned int pixbuf_y_stride;
unsigned int y;
unsigned int x;
unsigned int c;
@@ -677,17 +662,16 @@ static void png_pixels_pass ( struct image *image,
* as a bit depth of 8 with a stride of more than one.
*/
depth = png->depth;
- raw_stride = ( ( depth + 7 ) / 8 );
+ data_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 ) );
+ pixbuf_y_index = ( ( ( interlace->y_indent * png->pixbuf->width ) +
+ interlace->x_indent ) );
+ pixbuf_x_stride = interlace->x_stride;
+ pixbuf_y_stride = ( interlace->y_stride * png->pixbuf->width );
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,
@@ -697,11 +681,11 @@ static void png_pixels_pass ( struct image *image,
for ( y = 0 ; y < interlace->height ; y++ ) {
/* Skip filter byte */
- raw_offset++;
+ data++;
/* Iterate over each pixel in turn */
bits = depth;
- pixbuf_offset = pixbuf_y_offset;
+ pixbuf_index = pixbuf_y_index;
for ( x = 0 ; x < interlace->width ; x++ ) {
/* Extract sample value */
@@ -711,11 +695,8 @@ static void png_pixels_pass ( struct image *image,
current <<= depth;
bits -= depth;
if ( ! bits ) {
- copy_from_user ( &current,
- png->raw.data,
- raw_offset,
- sizeof ( current ) );
- raw_offset += raw_stride;
+ current = *data;
+ data += data_stride;
bits = 8;
}
@@ -746,17 +727,16 @@ static void png_pixels_pass ( struct image *image,
}
/* Store pixel */
- copy_to_user ( png->pixbuf->data, pixbuf_offset,
- &pixel, sizeof ( pixel ) );
- pixbuf_offset += pixbuf_x_stride;
+ png->pixbuf->data[pixbuf_index] = pixel;
+ pixbuf_index += pixbuf_x_stride;
}
/* Move to next output row */
- pixbuf_y_offset += pixbuf_y_stride;
+ pixbuf_y_index += pixbuf_y_stride;
}
/* Update offset */
- png->raw.offset = raw_offset;
+ png->raw.offset = ( ( ( const void * ) data ) - png->raw.data );
}
/**
@@ -904,8 +884,8 @@ static int png_chunk ( struct image *image, struct png_context *png,
*/
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;
+ const struct png_chunk_header *header;
+ const struct png_chunk_footer *footer;
size_t remaining;
size_t chunk_len;
int rc;
@@ -924,20 +904,19 @@ static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
/* Extract chunk header */
remaining = ( image->len - png->offset );
- if ( remaining < ( sizeof ( header ) + sizeof ( footer ) ) ) {
+ if ( remaining < ( sizeof ( *header ) + sizeof ( *footer ) ) ){
DBGC ( image, "PNG %s truncated chunk header/footer "
"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 );
+ header = ( image->data + png->offset );
+ png->offset += sizeof ( *header );
/* Validate chunk length */
- chunk_len = ntohl ( header.len );
- if ( chunk_len > ( remaining - sizeof ( header ) -
- sizeof ( footer ) ) ) {
+ chunk_len = ntohl ( header->len );
+ if ( chunk_len > ( remaining - sizeof ( *header ) -
+ sizeof ( *footer ) ) ) {
DBGC ( image, "PNG %s truncated chunk data at offset "
"%zd\n", image->name, png->offset );
rc = -EINVAL;
@@ -945,17 +924,17 @@ static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
}
/* Handle chunk */
- if ( ( rc = png_chunk ( image, png, header.type,
+ if ( ( rc = png_chunk ( image, png, header->type,
chunk_len ) ) != 0 )
goto err_chunk;
/* Move to next chunk */
- png->offset += ( chunk_len + sizeof ( footer ) );
+ 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 ) ) {
+ if ( header->type != htonl ( PNG_TYPE_IEND ) ) {
DBGC ( image, "PNG %s did not finish with IEND\n",
image->name );
rc = -EINVAL;
@@ -985,17 +964,17 @@ static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
* @ret rc Return status code
*/
static int png_probe ( struct image *image ) {
- struct png_signature signature;
+ const struct png_signature *signature;
/* Sanity check */
- if ( image->len < sizeof ( signature ) ) {
+ 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 ){
+ signature = image->data;
+ if ( memcmp ( signature, &png_signature, sizeof ( *signature ) ) != 0 ){
DBGC ( image, "PNG %s has invalid signature\n", image->name );
return -ENOEXEC;
}
diff --git a/src/image/pnm.c b/src/image/pnm.c
index f24b28841..489a43304 100644
--- a/src/image/pnm.c
+++ b/src/image/pnm.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <ipxe/image.h>
@@ -46,21 +47,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
char buf[ pnm->ascii_len + 1 /* NUL */ ];
char *endp;
+ char ch;
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] ) );
+ ch = *( ( ( const uint8_t * ) image->data ) + pnm->offset );
if ( in_comment ) {
- if ( buf[0] == '\n' )
+ if ( ch == '\n' )
in_comment = 0;
} else {
- if ( buf[0] == '#' ) {
+ if ( ch == '#' ) {
in_comment = 1;
- } else if ( ! isspace ( buf[0] ) ) {
+ } else if ( ! isspace ( ch ) ) {
break;
}
}
@@ -76,7 +77,7 @@ static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
/* 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 );
+ memcpy ( buf, ( image->data + pnm->offset ), len );
buf[len] = '\0';
/* Parse value and update offset */
@@ -104,7 +105,7 @@ static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
* @ret value Value, or negative error
*/
static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
- uint8_t value;
+ const uint8_t *value;
/* Sanity check */
if ( pnm->offset == image->len ) {
@@ -114,10 +115,8 @@ static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
}
/* Extract value */
- copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
- pnm->offset++;
-
- return value;
+ value = ( image->data + pnm->offset++ );
+ return *value;
}
/**
@@ -189,15 +188,15 @@ static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
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 pixels = pixbuf->pixels;
+ unsigned int index = 0;
unsigned int xpos = 0;
int scalar;
uint32_t composite;
- uint32_t rgb;
unsigned int i;
/* Fill pixel buffer */
- while ( offset < pixbuf->len ) {
+ while ( index < pixels ) {
/* Extract a scaled composite scalar value from the file */
composite = 0;
@@ -213,15 +212,12 @@ static int pnm_data ( struct image *image, struct pnm_context *pnm,
/* Extract 24-bit RGB values from composite value */
for ( i = 0 ; i < type->packing ; i++ ) {
- if ( offset >= pixbuf->len ) {
+ if ( index >= pixels ) {
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 );
+ pixbuf->data[index++] = type->rgb ( composite, i );
if ( ++xpos == pixbuf->width ) {
xpos = 0;
break;
@@ -287,19 +283,19 @@ static struct pnm_type pnm_types[] = {
* @ret type PNM image type, or NULL if not found
*/
static struct pnm_type * pnm_type ( struct image *image ) {
- struct pnm_signature signature;
+ const 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 ) );
+ assert ( image->len >= sizeof ( *signature ) );
+ signature = image->data;
/* 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 )
+ if ( type->type == signature->type )
return type;
}
return NULL;
@@ -390,23 +386,23 @@ static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
* @ret rc Return status code
*/
static int pnm_probe ( struct image *image ) {
- struct pnm_signature signature;
+ const struct pnm_signature *signature;
/* Sanity check */
- if ( image->len < sizeof ( signature ) ) {
+ 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 ) ) ) ) {
+ signature = image->data;
+ 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 );
+ DBGC ( image, "PNM %s is type %c\n", image->name, signature->type );
return 0;
}
diff --git a/src/image/script.c b/src/image/script.c
index 9e8566bc5..57662b788 100644
--- a/src/image/script.c
+++ b/src/image/script.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -69,7 +70,7 @@ static int process_script ( struct image *image,
size_t line_offset;
char *label;
char *command;
- off_t eol;
+ const void *eol;
size_t frag_len;
char *tmp;
int rc;
@@ -81,11 +82,13 @@ static int process_script ( struct image *image,
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 );
+ eol = memchr ( ( image->data + script_offset ), '\n',
+ ( image->len - script_offset ) );
+ if ( eol ) {
+ frag_len = ( ( eol - image->data ) - script_offset );
+ } else {
+ frag_len = ( image->len - script_offset );
+ }
/* Allocate buffer for line */
tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) );
@@ -96,8 +99,8 @@ static int process_script ( struct image *image,
line = tmp;
/* Copy line */
- copy_from_user ( ( line + len ), image->data, script_offset,
- frag_len );
+ memcpy ( ( line + len ), ( image->data + script_offset ),
+ frag_len );
len += frag_len;
/* Move to next line in script */
@@ -220,20 +223,24 @@ static int script_probe ( struct image *image ) {
static const char ipxe_magic[] = "#!ipxe";
static const char gpxe_magic[] = "#!gpxe";
static_assert ( sizeof ( ipxe_magic ) == sizeof ( gpxe_magic ) );
- char test[ sizeof ( ipxe_magic ) - 1 /* NUL */
- + 1 /* terminating space */];
+ const struct {
+ char magic[ sizeof ( ipxe_magic ) - 1 /* NUL */ ];
+ char space;
+ } __attribute__ (( packed )) *test;
/* Sanity check */
- if ( image->len < sizeof ( test ) ) {
+ 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 ] ) ) ) {
+ test = image->data;
+ if ( ! ( ( ( memcmp ( test->magic, ipxe_magic,
+ sizeof ( test->magic ) ) == 0 ) ||
+ ( memcmp ( test->magic, gpxe_magic,
+ sizeof ( test->magic ) ) == 0 ) ) &&
+ isspace ( test->space ) ) ) {
DBGC ( image, "Invalid magic signature\n" );
return -ENOEXEC;
}
@@ -346,10 +353,7 @@ static int goto_exec ( int argc, char **argv ) {
}
/** "goto" command */
-struct command goto_command __command = {
- .name = "goto",
- .exec = goto_exec,
-};
+COMMAND ( goto, goto_exec );
/** "prompt" options */
struct prompt_options {
@@ -412,7 +416,4 @@ static int prompt_exec ( int argc, char **argv ) {
}
/** "prompt" command */
-struct command prompt_command __command = {
- .name = "prompt",
- .exec = prompt_exec,
-};
+COMMAND ( prompt, prompt_exec );
diff --git a/src/image/segment.c b/src/image/segment.c
index 2d0f2f0fc..d90da709c 100644
--- a/src/image/segment.c
+++ b/src/image/segment.c
@@ -30,9 +30,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+#include <string.h>
#include <errno.h>
#include <ipxe/uaccess.h>
-#include <ipxe/io.h>
+#include <ipxe/memmap.h>
#include <ipxe/errortab.h>
#include <ipxe/segment.h>
@@ -57,39 +58,46 @@ struct errortab segment_errors[] __errortab = {
* @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;
+int prep_segment ( void *segment, size_t filesz, size_t memsz ) {
+ struct memmap_region region;
+ physaddr_t start = virt_to_phys ( segment );
+ physaddr_t mid = ( start + filesz );
+ physaddr_t end = ( start + memsz );
+ physaddr_t max;
- DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end );
+ DBGC ( segment, "SEGMENT [%#08lx,%#08lx,%#08lx)\n", start, mid, end );
- /* Sanity check */
+ /* Check for malformed lengths */
if ( filesz > memsz ) {
- DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end );
+ DBGC ( segment, "SEGMENT [%#08lx,%#08lx,%#08lx) is "
+ "malformed\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 );
+ /* Zero-length segments do not need a memory region */
+ if ( memsz == 0 )
+ return 0;
+ max = ( end - 1 );
- /* 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;
- }
+ /* Check for address space overflow */
+ if ( max < start ) {
+ DBGC ( segment, "SEGMENT [%#08lx,%#08lx,%#08lx) wraps "
+ "around\n", start, mid, end );
+ return -EINVAL;
+ }
+
+ /* Describe region containing this segment */
+ memmap_describe ( start, 1, &region );
+ DBGC_MEMMAP ( segment, &region );
+
+ /* Fail unless region is usable and sufficiently large */
+ if ( ( ! memmap_is_usable ( &region ) ) || ( region.max < max ) ) {
+ DBGC ( segment, "SEGMENT [%#08lx,%#08lx,%#08lx) does not fit "
+ "into available memory\n", start, mid, end );
+ return -ERANGE_SEGMENT;
}
- /* No suitable memory region found */
- DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n",
- start, mid, end );
- return -ERANGE_SEGMENT;
+ /* Found valid region: zero bss and return */
+ memset ( ( segment + filesz ), 0, ( memsz - filesz ) );
+ return 0;
}
diff --git a/src/image/zlib.c b/src/image/zlib.c
index a42c47e1b..f0f8f5622 100644
--- a/src/image/zlib.c
+++ b/src/image/zlib.c
@@ -27,7 +27,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <assert.h>
#include <ipxe/deflate.h>
-#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/zlib.h>
@@ -41,11 +40,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Extract compressed data to image
*
* @v format Compression format code
- * @v in Compressed input chunk
+ * @v data Compressed input data
+ * @v len Length of compressed input data
* @v extracted Extracted image
* @ret rc Return status code
*/
-int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in,
+int zlib_deflate ( enum deflate_format format, const void *data, size_t len,
struct image *extracted ) {
struct deflate *deflate;
struct deflate_chunk out;
@@ -64,23 +64,22 @@ int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in,
/* (Re)initialise decompressor */
deflate_init ( deflate, format );
- /* (Re)initialise input chunk */
- in->offset = 0;
-
/* Initialise output chunk */
- deflate_chunk_init ( &out, extracted->data, 0, extracted->len );
+ deflate_chunk_init ( &out, extracted->rwdata, 0,
+ extracted->len );
/* Decompress data */
- if ( ( rc = deflate_inflate ( deflate, in, &out ) ) != 0 ) {
- DBGC ( extracted, "ZLIB %p could not decompress: %s\n",
- extracted, strerror ( rc ) );
+ if ( ( rc = deflate_inflate ( deflate, data, len,
+ &out ) ) != 0 ) {
+ DBGC ( extracted, "ZLIB %s could not decompress: %s\n",
+ extracted->name, strerror ( rc ) );
goto err_inflate;
}
/* Check that decompression is valid */
if ( ! deflate_finished ( deflate ) ) {
- DBGC ( extracted, "ZLIB %p decompression incomplete\n",
- extracted );
+ DBGC ( extracted, "ZLIB %s decompression incomplete\n",
+ extracted->name );
rc = -EINVAL;
goto err_unfinished;
}
@@ -91,8 +90,8 @@ int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in,
/* Otherwise, resize output image and retry */
if ( ( rc = image_set_len ( extracted, out.offset ) ) != 0 ) {
- DBGC ( extracted, "ZLIB %p could not resize: %s\n",
- extracted, strerror ( rc ) );
+ DBGC ( extracted, "ZLIB %s could not resize: %s\n",
+ extracted->name, strerror ( rc ) );
goto err_set_size;
}
}
@@ -116,14 +115,11 @@ int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in,
* @ret rc Return status code
*/
static int zlib_extract ( struct image *image, struct image *extracted ) {
- struct deflate_chunk in;
int rc;
- /* Initialise input chunk */
- deflate_chunk_init ( &in, image->data, 0, image->len );
-
/* Decompress image */
- if ( ( rc = zlib_deflate ( DEFLATE_ZLIB, &in, extracted ) ) != 0 )
+ if ( ( rc = zlib_deflate ( DEFLATE_ZLIB, image->data, image->len,
+ extracted ) ) != 0 )
return rc;
return 0;
@@ -136,18 +132,18 @@ static int zlib_extract ( struct image *image, struct image *extracted ) {
* @ret rc Return status code
*/
static int zlib_probe ( struct image *image ) {
- union zlib_magic magic;
+ const union zlib_magic *magic;
/* Sanity check */
- if ( image->len < sizeof ( magic ) ) {
- DBGC ( image, "ZLIB %p image too short\n", image );
+ if ( image->len < sizeof ( *magic ) ) {
+ DBGC ( image, "ZLIB %s image too short\n", image->name );
return -ENOEXEC;
}
+ magic = image->data;
/* Check magic header */
- copy_from_user ( &magic, image->data, 0, sizeof ( magic ) );
- if ( ! zlib_magic_is_valid ( &magic ) ) {
- DBGC ( image, "ZLIB %p invalid magic data\n", image );
+ if ( ! zlib_magic_is_valid ( magic ) ) {
+ DBGC ( image, "ZLIB %s invalid magic data\n", image->name );
return -ENOEXEC;
}