ipxe_1.0.0+git-20180124.fbe8c52d/0000775000175000017500000000000013232107733015407 5ustar paelzerpaelzeripxe_1.0.0+git-20180124.fbe8c52d/README0000664000175000017500000000016113145251043016262 0ustar paelzerpaelzeriPXE README File Quick start guide: cd src make For any more detailed instructions, see http://ipxe.org ipxe_1.0.0+git-20180124.fbe8c52d/COPYING.UBDL0000664000175000017500000000556313177006616017206 0ustar paelzerpaelzerUNMODIFIED BINARY DISTRIBUTION LICENCE PREAMBLE The GNU General Public License provides a legal guarantee that software covered by it remains free (in the sense of freedom, not price). It achieves this guarantee by imposing obligations on anyone who chooses to distribute the software. Some of these obligations may be seen as unnecessarily burdensome. In particular, when the source code for the software is already publicly and freely available, there is minimal value in imposing upon each distributor the obligation to provide the complete source code (or an equivalent written offer to provide the complete source code). This Licence allows for the distribution of unmodified binaries built from publicly available source code, without imposing the obligations of the GNU General Public License upon anyone who chooses to distribute only the unmodified binaries built from that source code. The extra permissions granted by this Licence apply only to unmodified binaries built from source code which has already been made available to the public in accordance with the terms of the GNU General Public Licence. Nothing in this Licence allows for the creation of closed-source modified versions of the Program. Any modified versions of the Program are subject to the usual terms and conditions of the GNU General Public License. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION This Licence applies to any Program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this Unmodified Binary Distribution Licence. All terms used in the text of this Licence are to be interpreted as they are used in version 2 of the GNU General Public License as published by the Free Software Foundation. If you have made this Program available to the public in both source code and executable form in accordance with 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, then you are hereby granted an additional permission to use, copy, and distribute the unmodified executable form of this Program (the "Unmodified Binary") without restriction, including the right to permit persons to whom the Unmodified Binary is furnished to do likewise, subject to the following conditions: - when started running, the Program must display an announcement which includes the details of your existing publication of the Program made in accordance with the terms of the GNU General Public License. For example, the Program could display the URL of the publicly available source code from which the Unmodified Binary was built. - when exercising your right to grant permissions under this Licence, you do not need to refer directly to the text of this Licence, but you may not grant permissions beyond those granted to you by this Licence. ipxe_1.0.0+git-20180124.fbe8c52d/src/0000775000175000017500000000000013232107677016205 5ustar paelzerpaelzeripxe_1.0.0+git-20180124.fbe8c52d/src/image/0000775000175000017500000000000013177006616017266 5ustar paelzerpaelzeripxe_1.0.0+git-20180124.fbe8c52d/src/image/embedded.c0000664000175000017500000000474713177006616021177 0ustar paelzerpaelzer/** @file * * Embedded image support * * Embedded images are images built into the iPXE binary and do not require * fetching over the network. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include /* Raw image data for all embedded images */ #undef EMBED #define EMBED( _index, _path, _name ) \ extern char embedded_image_ ## _index ## _data[]; \ extern char embedded_image_ ## _index ## _len[]; \ __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nembedded_image_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ "\nembedded_image_" #_index "_end:\n\t" \ ".equ embedded_image_" #_index "_len, " \ "( embedded_image_" #_index "_end - " \ " embedded_image_" #_index "_data )\n\t" \ ".previous\n\t" ); EMBED_ALL /* Image structures for all embedded images */ #undef EMBED #define EMBED( _index, _path, _name ) { \ .refcnt = REF_INIT ( ref_no_free ), \ .name = _name, \ .data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \ .len = ( size_t ) embedded_image_ ## _index ## _len, \ }, static struct image embedded_images[] = { EMBED_ALL }; /** * Register all embedded images */ static void embedded_init ( void ) { int i; struct image *image; void *data; int rc; /* Skip if we have no embedded images */ if ( ! sizeof ( embedded_images ) ) return; /* Fix up data pointers and register images */ for ( i = 0 ; i < ( int ) ( sizeof ( embedded_images ) / sizeof ( embedded_images[0] ) ) ; i++ ) { image = &embedded_images[i]; /* virt_to_user() cannot be used in a static * initialiser, so we cast the pointer to a userptr_t * in the initialiser and fix it up here. (This will * actually be a no-op on most platforms.) */ data = ( ( void * ) image->data ); image->data = virt_to_user ( data ); DBG ( "Embedded image \"%s\": %zd bytes at %p\n", image->name, image->len, data ); if ( ( rc = register_image ( image ) ) != 0 ) { DBG ( "Could not register embedded image \"%s\": " "%s\n", image->name, strerror ( rc ) ); return; } } /* Select the first image */ image = &embedded_images[0]; if ( ( rc = image_select ( image ) ) != 0 ) { DBG ( "Could not select embedded image \"%s\": %s\n", image->name, strerror ( rc ) ); return; } } /** Embedded image initialisation function */ struct init_fn embedded_init_fn __init_fn ( INIT_LATE ) = { .initialise = embedded_init, }; ipxe_1.0.0+git-20180124.fbe8c52d/src/image/elf.c0000664000175000017500000001406313177006616020204 0ustar paelzerpaelzer/* * Copyright (C) 2007 Michael Brown . * * 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 * * ELF image format * * A "pure" ELF image is not a bootable image. There are various * bootable formats based upon ELF (e.g. Multiboot), which share * common ELF-related functionality. */ #include #include #include #include #include #include /** * Load ELF segment into memory * * @v image ELF file * @v phdr ELF program header * @v dest Destination address * @ret rc Return status code */ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, physaddr_t dest ) { userptr_t buffer = phys_to_user ( 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 ), 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 ) ); return rc; } /* Copy image to segment */ memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); return 0; } /** * Process ELF segment * * @v image ELF file * @v ehdr ELF executable header * @v phdr ELF program header * @v process Segment processor * @ret entry Entry point, if found * @ret max Maximum used address * @ret rc Return status code */ static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, int ( * process ) ( struct image *image, Elf_Phdr *phdr, physaddr_t dest ), physaddr_t *entry, physaddr_t *max ) { physaddr_t dest; physaddr_t end; unsigned long e_offset; int rc; /* Do nothing for non-PT_LOAD segments */ if ( phdr->p_type != PT_LOAD ) return 0; /* Check segment lies within image */ if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) { DBGC ( image, "ELF %p segment outside image\n", image ); return -ENOEXEC; } /* Find start address: use physical address for preference, * fall back to virtual address if no physical address * supplied. */ dest = phdr->p_paddr; if ( ! dest ) dest = phdr->p_vaddr; if ( ! dest ) { DBGC ( image, "ELF %p segment loads to physical address 0\n", image ); return -ENOEXEC; } end = ( dest + phdr->p_memsz ); /* Update maximum used address, if applicable */ if ( end > *max ) *max = end; /* Process segment */ if ( ( rc = process ( image, phdr, dest ) ) != 0 ) return rc; /* Set execution address, if it lies within this segment */ if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) { *entry = ehdr->e_entry; DBGC ( image, "ELF %p found physical entry point at %lx\n", image, *entry ); } else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) ) < phdr->p_filesz ) { if ( ! *entry ) { *entry = ( dest + e_offset ); DBGC ( image, "ELF %p found virtual entry point at %lx" " (virt %lx)\n", image, *entry, ( ( unsigned long ) ehdr->e_entry ) ); } } return 0; } /** * Process ELF segments * * @v image ELF file * @v ehdr ELF executable header * @v process Segment processor * @ret entry Entry point, if found * @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, physaddr_t dest ), physaddr_t *entry, physaddr_t *max ) { Elf_Phdr phdr; Elf_Off phoff; unsigned int phnum; int rc; /* Initialise maximum used address */ *max = 0; /* Invalidate entry point */ *entry = 0; /* 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 ); return -ENOEXEC; } copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); 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 ) ); return -ENOEXEC; } return 0; } /** * Load ELF image into memory * * @v image ELF file * @ret entry Entry point * @ret max Maximum used address * @ret rc Return status code */ int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { static const uint8_t e_ident[] = { [EI_MAG0] = ELFMAG0, [EI_MAG1] = ELFMAG1, [EI_MAG2] = ELFMAG2, [EI_MAG3] = ELFMAG3, [EI_CLASS] = ELFCLASS, }; Elf_Ehdr ehdr; int rc; /* Read ELF header */ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident, sizeof ( e_ident ) ) != 0 ) { DBGC ( image, "ELF %p has invalid signature\n", image ); return -ENOEXEC; } /* Load ELF segments into memory */ if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment, entry, max ) ) != 0 ) return rc; return 0; } ipxe_1.0.0+git-20180124.fbe8c52d/src/image/segment.c0000664000175000017500000000553213177006616021101 0ustar paelzerpaelzer/* * Copyright (C) 2007 Michael Brown . * * 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 * * Executable image segments * */ #include #include #include #include #include /** * Segment-specific error messages * * This error happens sufficiently often to merit a user-friendly * description. */ #define ERANGE_SEGMENT __einfo_error ( EINFO_ERANGE_SEGMENT ) #define EINFO_ERANGE_SEGMENT \ __einfo_uniqify ( EINFO_ERANGE, 0x01, "Requested memory not available" ) struct errortab segment_errors[] __errortab = { __einfo_errortab ( EINFO_ERANGE_SEGMENT ), }; /** * Prepare segment for loading * * @v segment Segment start * @v filesz Size of the "allocated bytes" portion of the segment * @v memsz Size of the segment * @ret rc Return status code */ int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) { struct memory_map memmap; physaddr_t start = user_to_phys ( segment, 0 ); physaddr_t mid = user_to_phys ( segment, filesz ); physaddr_t end = user_to_phys ( segment, memsz ); unsigned int i; DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end ); /* Sanity check */ if ( filesz > memsz ) { DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end ); return -EINVAL; } /* Get a fresh memory map. This allows us to automatically * avoid treading on any regions that Etherboot is currently * editing out of the memory map. */ get_memmap ( &memmap ); /* Look for a suitable memory region */ for ( i = 0 ; i < memmap.count ; i++ ) { if ( ( start >= memmap.regions[i].start ) && ( end <= memmap.regions[i].end ) ) { /* Found valid region: zero bss and return */ memset_user ( segment, filesz, 0, ( memsz - filesz ) ); return 0; } } /* No suitable memory region found */ DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n", start, mid, end ); return -ERANGE_SEGMENT; } ipxe_1.0.0+git-20180124.fbe8c52d/src/image/pnm.c0000664000175000017500000002277713177006616020243 0ustar paelzerpaelzer/* * Copyright (C) 2013 Michael Brown . * * 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 * * Portable anymap format (PNM) * */ #include #include #include #include #include #include /** * Extract PNM ASCII value * * @v image PNM image * @v pnm PNM context * @ret value Value, or negative error */ static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) { char buf[ pnm->ascii_len + 1 /* NUL */ ]; char *endp; size_t len; int value; int in_comment = 0; /* Skip any leading whitespace and comments */ for ( ; pnm->offset < image->len ; pnm->offset++ ) { copy_from_user ( &buf[0], image->data, pnm->offset, sizeof ( buf[0] ) ); if ( in_comment ) { if ( buf[0] == '\n' ) in_comment = 0; } else { if ( buf[0] == '#' ) { in_comment = 1; } else if ( ! isspace ( buf[0] ) ) { break; } } } /* Fail if no value is present */ len = ( image->len - pnm->offset ); if ( len == 0 ) { DBGC ( image, "PNM %s ran out of ASCII data\n", image->name ); return -EINVAL; } /* Copy ASCII value to buffer and ensure string is NUL-terminated */ if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) ) len = ( sizeof ( buf ) - 1 /* NUL */ ); copy_from_user ( buf, image->data, pnm->offset, len ); buf[len] = '\0'; /* Parse value and update offset */ value = strtoul ( buf, &endp, 0 ); pnm->offset += ( endp - buf ); /* Check and skip terminating whitespace character, if present */ if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) { if ( ! isspace ( *endp ) ) { DBGC ( image, "PNM %s invalid ASCII integer\n", image->name ); return -EINVAL; } pnm->offset++; } return value; } /** * Extract PNM binary value * * @v image PNM image * @v pnm PNM context * @ret value Value, or negative error */ static int pnm_binary ( struct image *image, struct pnm_context *pnm ) { uint8_t value; /* Sanity check */ if ( pnm->offset == image->len ) { DBGC ( image, "PNM %s ran out of binary data\n", image->name ); return -EINVAL; } /* Extract value */ copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) ); pnm->offset++; return value; } /** * Scale PNM scalar value * * @v image PNM image * @v pnm PNM context * @v value Raw value * @ret value Scaled value (in range 0-255) */ static int pnm_scale ( struct image *image, struct pnm_context *pnm, unsigned int value ) { if ( value > pnm->max ) { DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n", image->name, value, pnm->max ); return -EINVAL; } return ( ( 255 * value ) / pnm->max ); } /** * Convert PNM bitmap composite value to RGB * * @v composite Composite value * @v index Pixel index within this composite value * @ret rgb 24-bit RGB value */ static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) { /* Composite value is an 8-bit bitmask */ return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff ); } /** * Convert PNM greymap composite value to RGB * * @v composite Composite value * @v index Pixel index within this composite value * @ret rgb 24-bit RGB value */ static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){ /* Composite value is an 8-bit greyscale value */ return ( ( composite << 16 ) | ( composite << 8 ) | composite ); } /** * Convert PNM pixmap composite value to RGB * * @v composite Composite value * @v index Pixel index within this composite value * @ret rgb 24-bit RGB value */ static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) { /* Composite value is already an RGB value */ return composite; } /** * Extract PNM pixel data * * @v image PNM image * @v pnm PNM context * @v pixbuf Pixel buffer * @ret rc Return status code */ static int pnm_data ( struct image *image, struct pnm_context *pnm, struct pixel_buffer *pixbuf ) { struct pnm_type *type = pnm->type; size_t offset = 0; unsigned int xpos = 0; int scalar; uint32_t composite; uint32_t rgb; unsigned int i; /* Fill pixel buffer */ while ( offset < pixbuf->len ) { /* Extract a scaled composite scalar value from the file */ composite = 0; for ( i = 0 ; i < type->depth ; i++ ) { scalar = type->scalar ( image, pnm ); if ( scalar < 0 ) return scalar; scalar = pnm_scale ( image, pnm, scalar ); if ( scalar < 0 ) return scalar; composite = ( ( composite << 8 ) | scalar ); } /* Extract 24-bit RGB values from composite value */ for ( i = 0 ; i < type->packing ; i++ ) { if ( offset >= pixbuf->len ) { DBGC ( image, "PNM %s has too many pixels\n", image->name ); return -EINVAL; } rgb = type->rgb ( composite, i ); copy_to_user ( pixbuf->data, offset, &rgb, sizeof ( rgb ) ); offset += sizeof ( rgb ); if ( ++xpos == pixbuf->width ) { xpos = 0; break; } } } return 0; } /** PNM image types */ static struct pnm_type pnm_types[] = { { .type = '1', .depth = 1, .packing = 1, .flags = PNM_BITMAP, .scalar = pnm_ascii, .rgb = pnm_bitmap, }, { .type = '2', .depth = 1, .packing = 1, .scalar = pnm_ascii, .rgb = pnm_greymap, }, { .type = '3', .depth = 3, .packing = 1, .scalar = pnm_ascii, .rgb = pnm_pixmap, }, { .type = '4', .depth = 1, .packing = 8, .flags = PNM_BITMAP, .scalar = pnm_binary, .rgb = pnm_bitmap, }, { .type = '5', .depth = 1, .packing = 1, .scalar = pnm_binary, .rgb = pnm_greymap, }, { .type = '6', .depth = 3, .packing = 1, .scalar = pnm_binary, .rgb = pnm_pixmap, }, }; /** * Determine PNM image type * * @v image PNM image * @ret type PNM image type, or NULL if not found */ static struct pnm_type * pnm_type ( struct image *image ) { struct pnm_signature signature; struct pnm_type *type; unsigned int i; /* Extract signature */ assert ( image->len >= sizeof ( signature ) ); copy_from_user ( &signature, image->data, 0, sizeof ( signature ) ); /* Check for supported types */ for ( i = 0 ; i < ( sizeof ( pnm_types ) / sizeof ( pnm_types[0] ) ) ; i++ ) { type = &pnm_types[i]; if ( type->type == signature.type ) return type; } return NULL; } /** * Convert PNM image to pixel buffer * * @v image PNM image * @v pixbuf Pixel buffer to fill in * @ret rc Return status code */ static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { struct pnm_context pnm; int width; int height; int max; int rc; /* Initialise PNM context */ pnm.type = pnm_type ( image ); if ( ! pnm.type ) { rc = -ENOTSUP; goto err_type; } pnm.offset = sizeof ( struct pnm_signature ); pnm.ascii_len = PNM_ASCII_LEN; /* Extract width */ if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) { rc = width; goto err_width; } /* Extract height */ if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) { rc = height; goto err_height; } /* Extract maximum scalar value, if not predefined */ if ( pnm.type->flags & PNM_BITMAP ) { pnm.max = ( ( 1 << pnm.type->packing ) - 1 ); pnm.ascii_len = 1; } else { if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) { rc = max; goto err_max; } pnm.max = max; } if ( pnm.max == 0 ) { DBGC ( image, "PNM %s has invalid maximum value 0\n", image->name ); rc = -EINVAL; goto err_max; } DBGC ( image, "PNM %s is type %c width %d height %d max %d\n", image->name, pnm.type->type, width, height, pnm.max ); /* Allocate pixel buffer */ *pixbuf = alloc_pixbuf ( width, height ); if ( ! *pixbuf ) { rc = -ENOMEM; goto err_alloc_pixbuf; } /* Extract pixel data */ if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 ) goto err_data; return 0; err_data: pixbuf_put ( *pixbuf ); err_alloc_pixbuf: err_max: err_height: err_width: err_type: return rc; } /** * Probe PNM image * * @v image PNM image * @ret rc Return status code */ static int pnm_probe ( struct image *image ) { struct pnm_signature signature; /* Sanity check */ if ( image->len < sizeof ( signature ) ) { DBGC ( image, "PNM %s is too short\n", image->name ); return -ENOEXEC; } /* Check signature */ copy_from_user ( &signature, image->data, 0, sizeof ( signature ) ); if ( ! ( ( signature.magic == PNM_MAGIC ) && ( isdigit ( signature.type ) ) && ( isspace ( signature.space ) ) ) ) { DBGC ( image, "PNM %s has invalid signature\n", image->name ); return -ENOEXEC; } DBGC ( image, "PNM %s is type %c\n", image->name, signature.type ); return 0; } /** PNM image type */ struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = { .name = "PNM", .probe = pnm_probe, .pixbuf = pnm_pixbuf, }; ipxe_1.0.0+git-20180124.fbe8c52d/src/image/png.c0000664000175000017500000006334713177006616020233 0ustar paelzerpaelzer/* * Copyright (C) 2014 Michael Brown . * * 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 ); #include #include #include #include #include #include #include #include #include /** @file * * Portable Network Graphics (PNG) format * * The PNG format is defined in RFC 2083. */ /** PNG context */ struct png_context { /** Offset within image */ size_t offset; /** Pixel buffer */ struct pixel_buffer *pixbuf; /** Bit depth */ unsigned int depth; /** Colour type */ unsigned int colour_type; /** Number of channels */ unsigned int channels; /** Number of interlace passes */ unsigned int passes; /** Palette, in iPXE's pixel buffer format */ uint32_t palette[PNG_PALETTE_COUNT]; /** Decompression buffer for raw PNG data */ struct deflate_chunk raw; /** Decompressor */ struct deflate deflate; }; /** A PNG interlace pass */ struct png_interlace { /** Pass number */ unsigned int pass; /** X starting indent */ unsigned int x_indent; /** Y starting indent */ unsigned int y_indent; /** X stride */ unsigned int x_stride; /** Y stride */ unsigned int y_stride; /** Width */ unsigned int width; /** Height */ unsigned int height; }; /** PNG file signature */ static struct png_signature png_signature = PNG_SIGNATURE; /** Number of interlacing passes */ static uint8_t png_interlace_passes[] = { [PNG_INTERLACE_NONE] = 1, [PNG_INTERLACE_ADAM7] = 7, }; /** * Transcribe PNG chunk type name (for debugging) * * @v type Chunk type * @ret name Chunk type name */ static const char * png_type_name ( uint32_t type ) { static union { uint32_t type; char name[ sizeof ( uint32_t ) + 1 /* NUL */ ]; } u; u.type = type; return u.name; } /** * Calculate PNG interlace pass parameters * * @v png PNG context * @v pass Pass number (0=first pass) * @v interlace Interlace pass to fill in */ static void png_interlace ( struct png_context *png, unsigned int pass, struct png_interlace *interlace ) { unsigned int grid_width_log2; unsigned int grid_height_log2; unsigned int x_indent; unsigned int y_indent; unsigned int x_stride_log2; unsigned int y_stride_log2; unsigned int x_stride; unsigned int y_stride; unsigned int width; unsigned int height; /* Sanity check */ assert ( png->passes > 0 ); /* Store pass number */ interlace->pass = pass; /* Calculate interlace grid dimensions */ grid_width_log2 = ( png->passes / 2 ); grid_height_log2 = ( ( png->passes - 1 ) / 2 ); /* Calculate starting indents */ interlace->x_indent = x_indent = ( ( pass & 1 ) ? ( 1 << ( grid_width_log2 - ( pass / 2 ) - 1 ) ) : 0 ); interlace->y_indent = y_indent = ( ( pass && ! ( pass & 1 ) ) ? ( 1 << ( grid_height_log2 - ( ( pass - 1 ) / 2 ) - 1 ) ) : 0); /* Calculate strides */ x_stride_log2 = ( grid_width_log2 - ( pass / 2 ) ); y_stride_log2 = ( grid_height_log2 - ( pass ? ( ( pass - 1 ) / 2 ) : 0 ) ); interlace->x_stride = x_stride = ( 1 << x_stride_log2 ); interlace->y_stride = y_stride = ( 1 << y_stride_log2 ); /* Calculate pass dimensions */ width = png->pixbuf->width; height = png->pixbuf->height; interlace->width = ( ( width - x_indent + x_stride - 1 ) >> x_stride_log2 ); interlace->height = ( ( height - y_indent + y_stride - 1 ) >> y_stride_log2 ); } /** * Calculate PNG pixel length * * @v png PNG context * @ret pixel_len Pixel length */ static unsigned int png_pixel_len ( struct png_context *png ) { return ( ( ( png->channels * png->depth ) + 7 ) / 8 ); } /** * Calculate PNG scanline length * * @v png PNG context * @v interlace Interlace pass * @ret scanline_len Scanline length (including filter byte) */ static size_t png_scanline_len ( struct png_context *png, struct png_interlace *interlace ) { return ( 1 /* Filter byte */ + ( ( interlace->width * png->channels * png->depth ) + 7 ) / 8); } /** * Handle PNG image header chunk * * @v image PNG image * @v png PNG context * @v len Chunk length * @ret rc Return status code */ static int png_image_header ( struct image *image, struct png_context *png, size_t len ) { struct png_image_header ihdr; struct png_interlace interlace; unsigned int pass; /* Sanity check */ if ( len != sizeof ( ihdr ) ) { DBGC ( image, "PNG %s invalid IHDR length %zd\n", image->name, len ); return -EINVAL; } if ( png->pixbuf ) { DBGC ( image, "PNG %s duplicate IHDR\n", image->name ); return -EINVAL; } /* Extract image header */ copy_from_user ( &ihdr, image->data, png->offset, len ); DBGC ( image, "PNG %s %dx%d depth %d type %d compression %d filter %d " "interlace %d\n", image->name, ntohl ( ihdr.width ), ntohl ( ihdr.height ), ihdr.depth, ihdr.colour_type, ihdr.compression, ihdr.filter, ihdr.interlace ); /* Sanity checks */ if ( ihdr.compression >= PNG_COMPRESSION_UNKNOWN ) { DBGC ( image, "PNG %s unknown compression method %d\n", image->name, ihdr.compression ); return -ENOTSUP; } if ( ihdr.filter >= PNG_FILTER_UNKNOWN ) { DBGC ( image, "PNG %s unknown filter method %d\n", image->name, ihdr.filter ); return -ENOTSUP; } if ( ihdr.interlace >= PNG_INTERLACE_UNKNOWN ) { DBGC ( image, "PNG %s unknown interlace method %d\n", image->name, ihdr.interlace ); return -ENOTSUP; } /* Allocate pixel buffer */ png->pixbuf = alloc_pixbuf ( ntohl ( ihdr.width ), ntohl ( ihdr.height ) ); if ( ! png->pixbuf ) { DBGC ( image, "PNG %s could not allocate pixel buffer\n", image->name ); return -ENOMEM; } /* Extract bit depth */ png->depth = ihdr.depth; if ( ( png->depth == 0 ) || ( ( png->depth & ( png->depth - 1 ) ) != 0 ) ) { DBGC ( image, "PNG %s invalid depth %d\n", image->name, png->depth ); return -EINVAL; } /* Calculate number of channels */ png->colour_type = ihdr.colour_type; png->channels = 1; if ( ! ( ihdr.colour_type & PNG_COLOUR_TYPE_PALETTE ) ) { if ( ihdr.colour_type & PNG_COLOUR_TYPE_RGB ) png->channels += 2; if ( ihdr.colour_type & PNG_COLOUR_TYPE_ALPHA ) png->channels += 1; } /* Calculate number of interlace passes */ png->passes = png_interlace_passes[ihdr.interlace]; /* Calculate length of raw data buffer */ for ( pass = 0 ; pass < png->passes ; pass++ ) { png_interlace ( png, pass, &interlace ); if ( interlace.width == 0 ) continue; png->raw.len += ( interlace.height * png_scanline_len ( png, &interlace ) ); } /* Allocate raw data buffer */ png->raw.data = umalloc ( png->raw.len ); if ( ! png->raw.data ) { DBGC ( image, "PNG %s could not allocate data buffer\n", image->name ); return -ENOMEM; } return 0; } /** * Handle PNG palette chunk * * @v image PNG image * @v png PNG context * @v len Chunk length * @ret rc Return status code */ static int png_palette ( struct image *image, struct png_context *png, size_t len ) { size_t offset = png->offset; struct png_palette_entry palette; unsigned int i; /* Populate palette */ for ( i = 0 ; i < ( sizeof ( png->palette ) / sizeof ( png->palette[0] ) ) ; i++ ) { /* Stop when we run out of palette data */ if ( len < sizeof ( palette ) ) break; /* Extract palette entry */ copy_from_user ( &palette, image->data, offset, sizeof ( palette ) ); png->palette[i] = ( ( palette.red << 16 ) | ( palette.green << 8 ) | ( palette.blue << 0 ) ); DBGC2 ( image, "PNG %s palette entry %d is %#06x\n", image->name, i, png->palette[i] ); /* Move to next entry */ offset += sizeof ( palette ); len -= sizeof ( palette ); } return 0; } /** * Handle PNG image data chunk * * @v image PNG image * @v png PNG context * @v len Chunk length * @ret rc Return status code */ static int png_image_data ( struct image *image, struct png_context *png, size_t len ) { struct deflate_chunk in; int rc; /* Deflate this chunk */ deflate_chunk_init ( &in, image->data, png->offset, ( png->offset + len ) ); if ( ( rc = deflate_inflate ( &png->deflate, &in, &png->raw ) ) != 0 ) { DBGC ( image, "PNG %s could not decompress: %s\n", image->name, strerror ( rc ) ); return rc; } return 0; } /** * Unfilter byte using the "None" filter * * @v current Filtered current byte * @v left Unfiltered left byte * @v above Unfiltered above byte * @v above_left Unfiltered above-left byte * @ret current Unfiltered current byte */ static unsigned int png_unfilter_none ( unsigned int current, unsigned int left __unused, unsigned int above __unused, unsigned int above_left __unused ) { return current; } /** * Unfilter byte using the "Sub" filter * * @v current Filtered current byte * @v left Unfiltered left byte * @v above Unfiltered above byte * @v above_left Unfiltered above-left byte * @ret current Unfiltered current byte */ static unsigned int png_unfilter_sub ( unsigned int current, unsigned int left, unsigned int above __unused, unsigned int above_left __unused ) { return ( current + left ); } /** * Unfilter byte using the "Up" filter * * @v current Filtered current byte * @v left Unfiltered left byte * @v above Unfiltered above byte * @v above_left Unfiltered above-left byte * @ret current Unfiltered current byte */ static unsigned int png_unfilter_up ( unsigned int current, unsigned int left __unused, unsigned int above, unsigned int above_left __unused ) { return ( current + above ); } /** * Unfilter byte using the "Average" filter * * @v current Filtered current byte * @v left Unfiltered left byte * @v above Unfiltered above byte * @v above_left Unfiltered above-left byte * @ret current Unfiltered current byte */ static unsigned int png_unfilter_average ( unsigned int current, unsigned int left, unsigned int above, unsigned int above_left __unused ) { return ( current + ( ( above + left ) >> 1 ) ); } /** * Paeth predictor function (defined in RFC 2083) * * @v a Pixel A * @v b Pixel B * @v c Pixel C * @ret predictor Predictor pixel */ static unsigned int png_paeth_predictor ( unsigned int a, unsigned int b, unsigned int c ) { unsigned int p; unsigned int pa; unsigned int pb; unsigned int pc; /* Algorithm as defined in RFC 2083 section 6.6 */ p = ( a + b - c ); pa = abs ( p - a ); pb = abs ( p - b ); pc = abs ( p - c ); if ( ( pa <= pb ) && ( pa <= pc ) ) { return a; } else if ( pb <= pc ) { return b; } else { return c; } } /** * Unfilter byte using the "Paeth" filter * * @v current Filtered current byte * @v above_left Unfiltered above-left byte * @v above Unfiltered above byte * @v left Unfiltered left byte * @ret current Unfiltered current byte */ static unsigned int png_unfilter_paeth ( unsigned int current, unsigned int left, unsigned int above, unsigned int above_left ) { return ( current + png_paeth_predictor ( left, above, above_left ) ); } /** A PNG filter */ struct png_filter { /** * Unfilter byte * * @v current Filtered current byte * @v left Unfiltered left byte * @v above Unfiltered above byte * @v above_left Unfiltered above-left byte * @ret current Unfiltered current byte */ unsigned int ( * unfilter ) ( unsigned int current, unsigned int left, unsigned int above, unsigned int above_left ); }; /** PNG filter types */ static struct png_filter png_filters[] = { [PNG_FILTER_BASIC_NONE] = { png_unfilter_none }, [PNG_FILTER_BASIC_SUB] = { png_unfilter_sub }, [PNG_FILTER_BASIC_UP] = { png_unfilter_up }, [PNG_FILTER_BASIC_AVERAGE] = { png_unfilter_average }, [PNG_FILTER_BASIC_PAETH] = { png_unfilter_paeth }, }; /** * Unfilter one interlace pass of PNG raw data * * @v image PNG image * @v png PNG context * @v interlace Interlace pass * @ret rc Return status code * * This routine may assume that it is impossible to overrun the raw * data buffer, since the size is determined by the image dimensions. */ static int png_unfilter_pass ( struct image *image, struct png_context *png, struct png_interlace *interlace ) { size_t offset = png->raw.offset; size_t pixel_len = png_pixel_len ( png ); size_t scanline_len = png_scanline_len ( png, interlace ); struct png_filter *filter; unsigned int scanline; unsigned int byte; uint8_t filter_type; uint8_t left; uint8_t above; uint8_t above_left; uint8_t current; /* On the first scanline of a pass, above bytes are assumed to * be zero. */ above = 0; /* Iterate over each scanline in turn */ for ( scanline = 0 ; scanline < interlace->height ; scanline++ ) { /* Extract filter byte and determine filter type */ copy_from_user ( &filter_type, png->raw.data, offset++, sizeof ( filter_type ) ); if ( filter_type >= ( sizeof ( png_filters ) / sizeof ( png_filters[0] ) ) ) { DBGC ( image, "PNG %s unknown filter type %d\n", image->name, filter_type ); return -ENOTSUP; } filter = &png_filters[filter_type]; assert ( filter->unfilter != NULL ); DBGC2 ( image, "PNG %s pass %d scanline %d filter type %d\n", image->name, interlace->pass, scanline, filter_type ); /* At the start of a line, both above-left and left * bytes are taken to be zero. */ left = 0; above_left = 0; /* Iterate over each byte (not pixel) in turn */ for ( byte = 0 ; byte < ( scanline_len - 1 ) ; byte++ ) { /* Extract predictor bytes, if applicable */ if ( byte >= pixel_len ) { copy_from_user ( &left, png->raw.data, ( offset - pixel_len ), sizeof ( left ) ); } if ( scanline > 0 ) { copy_from_user ( &above, png->raw.data, ( offset - scanline_len ), sizeof ( above ) ); } if ( ( scanline > 0 ) && ( byte >= pixel_len ) ) { copy_from_user ( &above_left, png->raw.data, ( offset - scanline_len - pixel_len ), sizeof ( above_left ) ); } /* Unfilter current byte */ copy_from_user ( ¤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 ) ); } } /* Update offset */ png->raw.offset = offset; return 0; } /** * Unfilter PNG raw data * * @v image PNG image * @v png PNG context * @ret rc Return status code * * This routine may assume that it is impossible to overrun the raw * data buffer, since the size is determined by the image dimensions. */ static int png_unfilter ( struct image *image, struct png_context *png ) { struct png_interlace interlace; unsigned int pass; int rc; /* Process each interlace pass */ png->raw.offset = 0; for ( pass = 0 ; pass < png->passes ; pass++ ) { /* Calculate interlace pass parameters */ png_interlace ( png, pass, &interlace ); /* Skip zero-width rows (which have no filter bytes) */ if ( interlace.width == 0 ) continue; /* Unfilter this pass */ if ( ( rc = png_unfilter_pass ( image, png, &interlace ) ) != 0 ) return rc; } assert ( png->raw.offset == png->raw.len ); return 0; } /** * Calculate PNG pixel component value * * @v raw Raw component value * @v alpha Alpha value * @v max Maximum raw/alpha value * @ret value Component value in range 0-255 */ static inline unsigned int png_pixel ( unsigned int raw, unsigned int alpha, unsigned int max ) { /* The basic calculation is 255*(raw/max)*(value/max). We use * fixed-point arithmetic (scaling up to the maximum range for * a 32-bit integer), in order to get the same results for * alpha blending as the test cases (produced using * ImageMagick). */ return ( ( ( ( ( 0xff00 * raw * alpha ) / max ) / max ) + 0x80 ) >> 8 ); } /** * Fill one interlace pass of PNG pixels * * @v image PNG image * @v png PNG context * @v interlace Interlace pass * * This routine may assume that it is impossible to overrun either the * raw data buffer or the pixel buffer, since the sizes of both are * determined by the image dimensions. */ static void png_pixels_pass ( struct image *image, struct png_context *png, struct png_interlace *interlace ) { size_t raw_offset = png->raw.offset; uint8_t channel[png->channels]; int is_indexed = ( png->colour_type & PNG_COLOUR_TYPE_PALETTE ); int is_rgb = ( png->colour_type & PNG_COLOUR_TYPE_RGB ); int has_alpha = ( png->colour_type & PNG_COLOUR_TYPE_ALPHA ); size_t pixbuf_y_offset; size_t pixbuf_offset; size_t pixbuf_x_stride; size_t pixbuf_y_stride; size_t raw_stride; unsigned int y; unsigned int x; unsigned int c; unsigned int bits; unsigned int depth; unsigned int max; unsigned int alpha; unsigned int raw; unsigned int value; uint8_t current = 0; uint32_t pixel; /* We only ever use the top byte of 16-bit pixels. Model this * as a bit depth of 8 with a stride of more than one. */ depth = png->depth; raw_stride = ( ( depth + 7 ) / 8 ); if ( depth > 8 ) depth = 8; max = ( ( 1 << depth ) - 1 ); /* Calculate pixel buffer offset and strides */ pixbuf_y_offset = ( ( ( interlace->y_indent * png->pixbuf->width ) + interlace->x_indent ) * sizeof ( pixel ) ); pixbuf_x_stride = ( interlace->x_stride * sizeof ( pixel ) ); pixbuf_y_stride = ( interlace->y_stride * png->pixbuf->width * sizeof ( pixel ) ); DBGC2 ( image, "PNG %s pass %d %dx%d at (%d,%d) stride (%d,%d)\n", image->name, interlace->pass, interlace->width, interlace->height, interlace->x_indent, interlace->y_indent, interlace->x_stride, interlace->y_stride ); /* Iterate over each scanline in turn */ for ( y = 0 ; y < interlace->height ; y++ ) { /* Skip filter byte */ raw_offset++; /* Iterate over each pixel in turn */ bits = depth; pixbuf_offset = pixbuf_y_offset; for ( x = 0 ; x < interlace->width ; x++ ) { /* Extract sample value */ for ( c = 0 ; c < png->channels ; c++ ) { /* Get sample value into high bits of current */ current <<= depth; bits -= depth; if ( ! bits ) { copy_from_user ( ¤t, png->raw.data, raw_offset, sizeof ( current ) ); raw_offset += raw_stride; bits = 8; } /* Extract sample value */ channel[c] = ( current >> ( 8 - depth ) ); } /* Convert to native pixel format */ if ( is_indexed ) { /* Indexed */ pixel = png->palette[channel[0]]; } else { /* Determine alpha value */ alpha = ( has_alpha ? channel[ png->channels - 1 ] : max ); /* Convert to RGB value */ pixel = 0; for ( c = 0 ; c < 3 ; c++ ) { raw = channel[ is_rgb ? c : 0 ]; value = png_pixel ( raw, alpha, max ); assert ( value <= 255 ); pixel = ( ( pixel << 8 ) | value ); } } /* Store pixel */ copy_to_user ( png->pixbuf->data, pixbuf_offset, &pixel, sizeof ( pixel ) ); pixbuf_offset += pixbuf_x_stride; } /* Move to next output row */ pixbuf_y_offset += pixbuf_y_stride; } /* Update offset */ png->raw.offset = raw_offset; } /** * Fill PNG pixels * * @v image PNG image * @v png PNG context * * This routine may assume that it is impossible to overrun either the * raw data buffer or the pixel buffer, since the sizes of both are * determined by the image dimensions. */ static void png_pixels ( struct image *image, struct png_context *png ) { struct png_interlace interlace; unsigned int pass; /* Process each interlace pass */ png->raw.offset = 0; for ( pass = 0 ; pass < png->passes ; pass++ ) { /* Calculate interlace pass parameters */ png_interlace ( png, pass, &interlace ); /* Skip zero-width rows (which have no filter bytes) */ if ( interlace.width == 0 ) continue; /* Unfilter this pass */ png_pixels_pass ( image, png, &interlace ); } assert ( png->raw.offset == png->raw.len ); } /** * Handle PNG image end chunk * * @v image PNG image * @v png PNG context * @v len Chunk length * @ret rc Return status code */ static int png_image_end ( struct image *image, struct png_context *png, size_t len ) { int rc; /* Sanity checks */ if ( len != 0 ) { DBGC ( image, "PNG %s invalid IEND length %zd\n", image->name, len ); return -EINVAL; } if ( ! png->pixbuf ) { DBGC ( image, "PNG %s missing pixel buffer (no IHDR?)\n", image->name ); return -EINVAL; } if ( ! deflate_finished ( &png->deflate ) ) { DBGC ( image, "PNG %s decompression not complete\n", image->name ); return -EINVAL; } if ( png->raw.offset != png->raw.len ) { DBGC ( image, "PNG %s incorrect decompressed length (expected " "%zd, got %zd)\n", image->name, png->raw.len, png->raw.offset ); return -EINVAL; } /* Unfilter raw data */ if ( ( rc = png_unfilter ( image, png ) ) != 0 ) return rc; /* Fill pixel buffer */ png_pixels ( image, png ); return 0; } /** A PNG chunk handler */ struct png_chunk_handler { /** Chunk type */ uint32_t type; /** * Handle chunk * * @v image PNG image * @v png PNG context * @v len Chunk length * @ret rc Return status code */ int ( * handle ) ( struct image *image, struct png_context *png, size_t len ); }; /** PNG chunk handlers */ static struct png_chunk_handler png_chunk_handlers[] = { { htonl ( PNG_TYPE_IHDR ), png_image_header }, { htonl ( PNG_TYPE_PLTE ), png_palette }, { htonl ( PNG_TYPE_IDAT ), png_image_data }, { htonl ( PNG_TYPE_IEND ), png_image_end }, }; /** * Handle PNG chunk * * @v image PNG image * @v png PNG context * @v type Chunk type * @v len Chunk length * @ret rc Return status code */ static int png_chunk ( struct image *image, struct png_context *png, uint32_t type, size_t len ) { struct png_chunk_handler *handler; unsigned int i; DBGC ( image, "PNG %s chunk type %s offset %zd length %zd\n", image->name, png_type_name ( type ), png->offset, len ); /* Handle according to chunk type */ for ( i = 0 ; i < ( sizeof ( png_chunk_handlers ) / sizeof ( png_chunk_handlers[0] ) ) ; i++ ) { handler = &png_chunk_handlers[i]; if ( handler->type == type ) return handler->handle ( image, png, len ); } /* Fail if unknown chunk type is critical */ if ( ! ( type & htonl ( PNG_CHUNK_ANCILLARY ) ) ) { DBGC ( image, "PNG %s unknown critical chunk type %s\n", image->name, png_type_name ( type ) ); return -ENOTSUP; } /* Ignore non-critical unknown chunk types */ return 0; } /** * Convert PNG image to pixel buffer * * @v image PNG image * @v pixbuf Pixel buffer to fill in * @ret rc Return status code */ static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { struct png_context *png; struct png_chunk_header header; struct png_chunk_footer footer; size_t remaining; size_t chunk_len; int rc; /* Allocate and initialise context */ png = zalloc ( sizeof ( *png ) ); if ( ! png ) { rc = -ENOMEM; goto err_alloc; } png->offset = sizeof ( struct png_signature ); deflate_init ( &png->deflate, DEFLATE_ZLIB ); /* Process chunks */ do { /* Extract chunk header */ remaining = ( image->len - png->offset ); if ( remaining < sizeof ( header ) ) { DBGC ( image, "PNG %s truncated chunk header at offset " "%zd\n", image->name, png->offset ); rc = -EINVAL; goto err_truncated; } copy_from_user ( &header, image->data, png->offset, sizeof ( header ) ); png->offset += sizeof ( header ); /* Validate chunk length */ chunk_len = ntohl ( header.len ); if ( remaining < ( sizeof ( header ) + chunk_len + sizeof ( footer ) ) ) { DBGC ( image, "PNG %s truncated chunk data/footer at " "offset %zd\n", image->name, png->offset ); rc = -EINVAL; goto err_truncated; } /* Handle chunk */ if ( ( rc = png_chunk ( image, png, header.type, chunk_len ) ) != 0 ) goto err_chunk; /* Move to next chunk */ png->offset += ( chunk_len + sizeof ( footer ) ); } while ( png->offset < image->len ); /* Check that we finished with an IEND chunk */ if ( header.type != htonl ( PNG_TYPE_IEND ) ) { DBGC ( image, "PNG %s did not finish with IEND\n", image->name ); rc = -EINVAL; goto err_iend; } /* Return pixel buffer */ *pixbuf = pixbuf_get ( png->pixbuf ); /* Success */ rc = 0; err_iend: err_chunk: err_truncated: pixbuf_put ( png->pixbuf ); ufree ( png->raw.data ); free ( png ); err_alloc: return rc; } /** * Probe PNG image * * @v image PNG image * @ret rc Return status code */ static int png_probe ( struct image *image ) { struct png_signature signature; /* Sanity check */ if ( image->len < sizeof ( signature ) ) { DBGC ( image, "PNG %s is too short\n", image->name ); return -ENOEXEC; } /* Check signature */ copy_from_user ( &signature, image->data, 0, sizeof ( signature ) ); if ( memcmp ( &signature, &png_signature, sizeof ( signature ) ) != 0 ){ DBGC ( image, "PNG %s has invalid signature\n", image->name ); return -ENOEXEC; } return 0; } /** PNG image type */ struct image_type png_image_type __image_type ( PROBE_NORMAL ) = { .name = "PNG", .probe = png_probe, .pixbuf = png_pixbuf, }; ipxe_1.0.0+git-20180124.fbe8c52d/src/image/script.c0000664000175000017500000002275313177006616020747 0ustar paelzerpaelzer/* * Copyright (C) 2007 Michael Brown . * * 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 * * iPXE scripts * */ #include #include #include #include #include #include #include #include #include #include #include #include /** Offset within current script * * This is a global in order to allow goto_exec() to update the * offset. */ static size_t script_offset; /** * Process script lines * * @v image Script * @v process_line Line processor * @v terminate Termination check * @ret rc Return status code */ static int process_script ( struct image *image, int ( * process_line ) ( struct image *image, size_t offset, const char *label, const char *command ), int ( * terminate ) ( int rc ) ) { size_t len = 0; char *line = NULL; size_t line_offset; char *label; char *command; off_t eol; size_t frag_len; char *tmp; int rc; /* Initialise script and line offsets */ script_offset = 0; line_offset = 0; do { /* Find length of next line, excluding any terminating '\n' */ eol = memchr_user ( image->data, script_offset, '\n', ( image->len - script_offset ) ); if ( eol < 0 ) eol = image->len; frag_len = ( eol - script_offset ); /* Allocate buffer for line */ tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) ); if ( ! tmp ) { rc = -ENOMEM; goto err_alloc; } line = tmp; /* Copy line */ copy_from_user ( ( line + len ), image->data, script_offset, frag_len ); len += frag_len; /* Move to next line in script */ script_offset += ( frag_len + 1 ); /* Strip trailing CR, if present */ if ( len && ( line[ len - 1 ] == '\r' ) ) len--; /* Handle backslash continuations */ if ( len && ( line[ len - 1 ] == '\\' ) ) { len--; rc = -EINVAL; continue; } /* Terminate line */ line[len] = '\0'; /* Split line into (optional) label and command */ command = line; while ( isspace ( *command ) ) command++; if ( *command == ':' ) { label = ++command; while ( *command && ! isspace ( *command ) ) command++; if ( *command ) *(command++) = '\0'; } else { label = NULL; } /* Process line */ rc = process_line ( image, line_offset, label, command ); if ( terminate ( rc ) ) goto err_process; /* Free line */ free ( line ); line = NULL; len = 0; /* Update line offset */ line_offset = script_offset; } while ( script_offset < image->len ); err_process: err_alloc: free ( line ); return rc; } /** * Terminate script processing on shell exit or command failure * * @v rc Line processing status * @ret terminate Terminate script processing */ static int terminate_on_exit_or_failure ( int rc ) { return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) || ( rc != 0 ) ); } /** * Execute script line * * @v image Script * @v offset Offset within script * @v label Label, or NULL * @v command Command * @ret rc Return status code */ static int script_exec_line ( struct image *image, size_t offset, const char *label __unused, const char *command ) { int rc; DBGC ( image, "[%04zx] $ %s\n", offset, command ); /* Execute command */ if ( ( rc = system ( command ) ) != 0 ) return rc; return 0; } /** * Execute script * * @v image Script * @ret rc Return status code */ static int script_exec ( struct image *image ) { size_t saved_offset; int rc; /* Temporarily de-register image, so that a "boot" command * doesn't throw us into an execution loop. */ unregister_image ( image ); /* Preserve state of any currently-running script */ saved_offset = script_offset; /* Process script */ rc = process_script ( image, script_exec_line, terminate_on_exit_or_failure ); /* Restore saved state */ script_offset = saved_offset; /* Re-register image (unless we have been replaced) */ if ( ! image->replacement ) register_image ( image ); return rc; } /** * Probe script image * * @v image Script * @ret rc Return status code */ static int script_probe ( struct image *image ) { static const char ipxe_magic[] = "#!ipxe"; static const char gpxe_magic[] = "#!gpxe"; linker_assert ( sizeof ( ipxe_magic ) == sizeof ( gpxe_magic ), magic_size_mismatch ); char test[ sizeof ( ipxe_magic ) - 1 /* NUL */ + 1 /* terminating space */]; /* Sanity check */ if ( image->len < sizeof ( test ) ) { DBGC ( image, "Too short to be a script\n" ); return -ENOEXEC; } /* Check for magic signature */ copy_from_user ( test, image->data, 0, sizeof ( test ) ); if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) || ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) && isspace ( test[ sizeof ( test ) - 1 ] ) ) ) { DBGC ( image, "Invalid magic signature\n" ); return -ENOEXEC; } return 0; } /** Script image type */ struct image_type script_image_type __image_type ( PROBE_NORMAL ) = { .name = "script", .probe = script_probe, .exec = script_exec, }; /** "goto" options */ struct goto_options {}; /** "goto" option list */ static struct option_descriptor goto_opts[] = {}; /** "goto" command descriptor */ static struct command_descriptor goto_cmd = COMMAND_DESC ( struct goto_options, goto_opts, 1, 1, "