diff options
| author | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
| commit | 8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch) | |
| tree | a8b359e59196be5b2e3862bed189107f4bc9975f /src/image | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [prefix] Make unlzma.S compatible with 386 class CPUs (diff) | |
| download | ipxe-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.c | 100 | ||||
| -rw-r--r-- | src/image/efi_image.c | 130 | ||||
| -rw-r--r-- | src/image/efi_siglist.c | 256 | ||||
| -rw-r--r-- | src/image/elf.c | 75 | ||||
| -rw-r--r-- | src/image/embedded.c | 24 | ||||
| -rw-r--r-- | src/image/gzip.c | 116 | ||||
| -rw-r--r-- | src/image/initrd.c | 399 | ||||
| -rw-r--r-- | src/image/lkrn.c | 368 | ||||
| -rw-r--r-- | src/image/pem.c | 25 | ||||
| -rw-r--r-- | src/image/png.c | 185 | ||||
| -rw-r--r-- | src/image/pnm.c | 56 | ||||
| -rw-r--r-- | src/image/script.c | 47 | ||||
| -rw-r--r-- | src/image/segment.c | 62 | ||||
| -rw-r--r-- | src/image/zlib.c | 46 |
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 ( ®ion, 0 ) { + DBGC_MEMMAP ( image, ®ion ); + 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, ®ion ) ) != 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 ( ®ion ) ) ) { + /* 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 ( ¤t, png->raw.data, - offset, sizeof ( current ) ); - current = filter->unfilter ( current, left, above, - above_left ); - copy_to_user ( png->raw.data, offset++, - ¤t, 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 ( ¤t, - 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, ®ion ); + DBGC_MEMMAP ( segment, ®ion ); + + /* Fail unless region is usable and sufficiently large */ + if ( ( ! memmap_is_usable ( ®ion ) ) || ( 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; } |
