| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) Helge Deller <deller@gmx.de> |
| */ |
| #ifdef __hpux |
| # define _LINUX_TYPES_H |
| #endif |
| #include "common.h" |
| #include "load.h" |
| |
| #include <stdio.h> |
| |
| |
| #ifndef NULL |
| #define NULL (void *)0 |
| #endif |
| |
| /* |
| * gzip declarations |
| */ |
| #ifdef IPL_LOADER |
| # include "bootloader.h" |
| #else |
| # include <setjmp.h> |
| # include <string.h> |
| # include <stdlib.h> |
| # define pa_memcpy memcpy |
| # define mark gzip_mark |
| # define release gzip_release |
| #endif |
| |
| # define gzip_mark(ptr) do { *ptr = 0UL; } while (0) |
| # define gzip_release(x) do { } while (0) |
| |
| static void pa_free(void *ptr) { } |
| #define pa_malloc malloc |
| |
| #define OF(args) args |
| #define STATIC static |
| |
| #define memzero(s, n) memset ((s), 0, (n)) |
| |
| typedef unsigned char uch; |
| typedef unsigned short ush; |
| typedef unsigned long ulg; |
| |
| #define WSIZE 0x8000 /* Window size must be at least 32k, */ |
| /* and a power of two */ |
| static uch window[WSIZE]; /* Sliding window buffer */ |
| |
| static unsigned outcnt = 0; /* bytes in output buffer */ |
| |
| /* gzip flag byte */ |
| #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ |
| #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ |
| #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ |
| #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ |
| #define COMMENT 0x10 /* bit 4 set: file comment present */ |
| #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ |
| #define RESERVED 0xC0 /* bit 6,7: reserved */ |
| |
| #define Assert(cond,msg) |
| #define Trace(x) |
| #define Tracev(x) |
| #define Tracevv(x) |
| #define Tracec(c,x) |
| #define Tracecv(c,x) |
| |
| static void flush_window (void); |
| static void error (char *); |
| static long bytes_out; |
| static uch *output_data, *output_limit; |
| |
| /* gzbuffer is the buffer in which we keep the uncompressed gzip data */ |
| #define GZBUFFER_SIZE (4096*2) |
| static unsigned char gzbuffer[GZBUFFER_SIZE]; |
| static int orig_fd; /* file descriptor for seekread */ |
| static int blocknr; |
| static int gzminp; |
| |
| static unsigned char get_byte(void) |
| { |
| if (gzminp == GZBUFFER_SIZE) { |
| blocknr++; |
| STRUCTREAD(orig_fd, gzbuffer, blocknr * GZBUFFER_SIZE); |
| gzminp = 0; |
| } |
| return gzbuffer[gzminp++]; |
| } |
| |
| static void unget_byte(void) |
| { |
| if (gzminp == 0) { |
| blocknr--; |
| STRUCTREAD(orig_fd, gzbuffer, blocknr * GZBUFFER_SIZE); |
| gzminp = GZBUFFER_SIZE; |
| } |
| gzminp--; |
| } |
| |
| static jmp_buf gunzip_env; |
| |
| #include "../lib/inflate.c" |
| |
| static void error (char *m) |
| { |
| printf ("\nDecompression error: %s\n", m); |
| longjmp (gunzip_env, 1); |
| } |
| |
| static void flush_window () |
| { |
| ulg c = crc; |
| unsigned n; |
| uch *in, ch; |
| in = window; |
| if (output_data + outcnt > output_limit) |
| error ("\nuncompressed image too long - wouldn't fit into destination"); |
| for (n = 0; n < outcnt; n++) { |
| ch = *output_data++ = *in++; |
| c = crc_32_tab[((int) c ^ ch) & 0xff] ^ (c >> 8); |
| } |
| crc = c; |
| |
| /* show progress indicator */ |
| printf("."); |
| |
| bytes_out += (ulg) outcnt; |
| outcnt = 0; |
| } |
| |
| static long decompress(char *outptr, char *outptrlim) |
| { |
| void *save_ptr; |
| static int first = 1; |
| |
| gzip_mark (&save_ptr); |
| if (setjmp (gunzip_env)) { |
| gzip_release (&save_ptr); |
| return -1; |
| } |
| output_data = (uch *)outptr; |
| output_limit = (uch *)outptrlim; |
| bytes_out = 0; |
| outcnt = 0; |
| crc = 0xffffffffL; |
| if (first) { |
| makecrc (); |
| first = 0; |
| } |
| gunzip (); |
| gzip_release (&save_ptr); |
| return bytes_out; |
| } |
| |
| |
| #define GZIP_MAGIC0 0x1f |
| #define GZIP_MAGIC1 0x8b |
| #define GZIP_MAGIC2 0x08 /* DEFLATED */ |
| |
| int prepare_GZIP_loadable(int fd, struct loadable *loadable, int *wide) |
| { |
| unsigned char flags; |
| long bytes_len; |
| char *orig_filename = "unknown"; |
| char *outptr, *outptrlim; |
| int i; |
| void *release_all_mallocs, *keep_unzipped; |
| |
| blocknr = 0; |
| gzminp = 0; |
| orig_fd = fd; |
| STRUCTREAD(orig_fd, gzbuffer, 0); |
| |
| if (gzbuffer[0] != GZIP_MAGIC0 || |
| gzbuffer[1] != GZIP_MAGIC1 || |
| gzbuffer[2] != GZIP_MAGIC2) |
| { |
| return PREPARE_CONTINUE; |
| } |
| |
| flags = gzbuffer[3]; /* general flags */ |
| if (flags & ORIG_NAME) |
| orig_filename = (char *) &gzbuffer[10]; |
| |
| mark(&release_all_mallocs); |
| #define MAX_GZ_LEN (100*1024*1024) |
| outptr = (char *) pa_malloc(MAX_GZ_LEN); |
| mark(&keep_unzipped); |
| |
| outptrlim = outptr + MAX_GZ_LEN; |
| if (0) |
| printf("uncompressing %s to 0x%p", orig_filename, outptr); |
| else |
| printf("uncompressing Linux kernel"); |
| |
| bytes_len = decompress(outptr, outptrlim); |
| if (bytes_len > MAX_GZ_LEN) |
| printf("\nERROR: Internal buffer MAX_GZ_LEN too small!"); |
| if (bytes_len < 0) { |
| release(release_all_mallocs); |
| return PREPARE_CONTINUE; |
| } |
| release(keep_unzipped); |
| |
| loadable->uncompressed_size = bytes_len; |
| loadable->uncompressed_data = outptr; |
| |
| if (0) |
| printf("\nGZIP compressed file size = %lu\n", bytes_len); |
| else |
| printf("\n"); |
| |
| /* try to detect real 32 or 64bit kernel now */ |
| i = prepare_ELF32_loadable(fd, loadable, wide); |
| if (i != PREPARE_OK) |
| i = prepare_ELF64_loadable(fd, loadable, wide); |
| |
| if (i != PREPARE_OK) |
| release(release_all_mallocs); |
| |
| return i; |
| } |