Imported Upstream version 3.12
[debian/elilo] / ia32 / gzip.c
1 /*
2  *  Copyright (C) 2001-2002 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *
5  *  Copyright (C) 2001 Silicon Graphics, Inc.
6  *      Contributed by Brent Casavant <bcasavan@sgi.com>
7  *
8  * This file is part of the ELILO, the EFI Linux boot loader.
9  *
10  *  ELILO is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  ELILO is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with ELILO; see the file COPYING.  If not, write to the Free
22  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23  *  02111-1307, USA.
24  *
25  * Please check out the elilo.txt for complete documentation on how
26  * to use this program.
27  */
28
29 #include <efi.h>
30 #include <efilib.h>
31
32 #include "elf.h"
33 #include "elilo.h"
34 #include "gzip.h"
35 #include "private.h"
36
37 #define LD_NAME L"gzip_ia32"
38
39 #define memzero(s, n)   Memset((VOID *)(s), 0, (n))
40 #define memcpy(a,b,n)   Memcpy((VOID *)(a),(b),(n))
41
42 /* size of output buffer */
43 #define WSIZE 0x8000            /* Window size must be at least 32k, */
44                                 /* and a power of two */
45 /* size of input buffer */
46 #define INBUFSIZE 0x8000
47
48 /*
49  * gzip declarations
50  */
51
52 #define OF(args)  args
53 #define FUNC_STATIC static
54
55 typedef unsigned char  uch;
56 typedef unsigned short ush;
57 typedef unsigned long  ulg;
58
59
60 typedef struct segment {
61         unsigned long addr;     /* start address */
62         unsigned long offset;   /* file offset   */
63         unsigned long size;     /* file size     */
64         unsigned long bss_sz;   /* BSS size      */
65         UINT8   flags;  /* indicates whether to load or not */
66 } segment_t;
67
68 #define CHUNK_FL_VALID          0x1
69 #define CHUNK_FL_LOAD           0x2
70
71 #define CHUNK_CAN_LOAD(n)       chunks[(n)].flags |= CHUNK_FL_LOAD
72 #define CHUNK_NO_LOAD(n)        chunks[(n)].flags &= ~CHUNK_FL_LOAD
73 #define CHUNK_IS_LOAD(n)        (chunks[(n)].flags & CHUNK_FL_LOAD)
74
75 #define CHUNK_VALIDATE(n)       chunks[(n)].flags |= CHUNK_FL_VALID
76 #define CHUNK_INVALIDATE(n)     chunks[(n)].flags = 0
77 #define CHUNK_IS_VALID(n)       (chunks[(n)].flags & CHUNK_FL_VALID)
78
79 /*
80  * static parameters to gzip helper functions
81  * we cannot use paramters because API was not
82  * designed that way
83  */
84 static segment_t *chunks;       /* holds the list of segments */
85 static segment_t *cur_chunk;
86 static UINTN nchunks;
87 static UINTN chunk;                 /* current segment */
88 static UINTN input_fd;
89 static VOID *kernel_entry, *kernel_base, *kernel_end;
90
91 static uch *inbuf;              /* input buffer (compressed data) */
92 static uch *window;             /* output buffer (uncompressed data) */
93 static unsigned long file_offset;       /* position in the file */
94
95 static unsigned insize = 0;  /* valid bytes in inbuf */
96 static unsigned inptr  = 0;   /* index of next byte to be processed in inbuf */
97 static unsigned outcnt = 0;  /* bytes in output buffer */
98
99 /* gzip flag byte */
100 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
101 #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
102 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
103 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
104 #define COMMENT      0x10 /* bit 4 set: file comment present */
105 #define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
106 #define RESERVED     0xC0 /* bit 6,7:   reserved */
107
108 #define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
109
110 /* Diagnostic functions */
111 #ifdef INFLATE_DEBUG
112 #  define Assert(cond,msg) {if(!(cond)) error(msg);}
113 int stderr;
114 #  define Trace(x) Print(L"line %d:\n", __LINE__);
115 #  define Tracev(x) {if (verbose) Print(L"line %d:\n", __LINE__) ;}
116 #  define Tracevv(x) {if (verbose>1) Print(L"line %d:\n", __LINE__)  ;}
117 #  define Tracec(c,x) {if (verbose && (c))  Print(L"line %d:\n", __LINE__) ;}
118 #  define Tracecv(c,x) {if (verbose>1 && (c))  Print(L"line %d:\n", __LINE__) ;}
119 #else
120 #  define Assert(cond,msg)
121 #  define Trace(x)
122 #  define Tracev(x)
123 #  define Tracevv(x)
124 #  define Tracec(c,x)
125 #  define Tracecv(c,x)
126 #endif
127
128 static int  fill_inbuf(void);
129 static void flush_window(void);
130 static void error(char *m);
131 static long bytes_out;
132 static void error(char *m);
133 static int error_return;
134
135 static void *
136 gzip_malloc(int size)
137 {
138         return (void *)alloc(size, 0);
139 }
140
141 static void
142 gzip_free(void *where)
143 {       
144         return free(where);
145 }
146
147 #include "inflate.c"
148
149 /*
150  * Fill the input buffer and return the first byte in it. This is called
151  * only when the buffer is empty and at least one byte is really needed.
152  */
153 int
154 fill_inbuf(void)
155 {
156         UINTN expected, nread;
157         EFI_STATUS status;
158
159         expected = nread = INBUFSIZE;
160
161         status = fops_read(input_fd, inbuf, &nread);
162         if (EFI_ERROR(status)) {
163                 error("elilo: Read failed");
164         }
165 #ifdef DEBUG_GZIP
166         DBG_PRT((L"%s : read %d bytes of %d bytes\n", LD_NAME, nread, expected));
167 #endif
168
169         insize = nread;
170         inptr = 1;
171
172         return inbuf[0];
173 }
174
175 /* ===========================================================================
176  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
177  * (Used for the decompressed data only.)
178  */
179
180 /*
181  * Run a set of bytes through the crc shift register.  If s is a NULL
182  * pointer, then initialize the crc shift register contents instead.
183  * Return the current crc in either case.
184  *
185  * Input:
186  *      S       pointer to bytes to pump through.
187  *      N       number of bytes in S[].
188  */
189 unsigned long
190 updcrc(unsigned char *s, unsigned n)
191 {
192         register unsigned long c;
193         /* crc is defined in inflate.c */
194
195         if (!s) {
196                 c = 0xffffffffL;
197         } else {
198                 c = crc;
199                 while (n--) {
200                         c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
201                 }
202         }
203         crc = c;
204         return c ^ 0xffffffffUL;       /* (instead of ~c for 64-bit machines) */
205 }
206
207
208 /*
209  * Clear input and output buffers
210  */
211 void
212 clear_bufs(void)
213 {
214         outcnt = 0;
215         inptr = 0;
216         chunk = 0;
217         cur_chunk = NULL;
218         file_offset = 0;
219 }
220
221
222 static INTN
223 is_valid_header(Elf32_Ehdr *ehdr)
224 {
225         UINT16 type, machine;
226
227         type    = ehdr->e_type;
228         machine = ehdr->e_machine;
229         
230         VERB_PRT(3, Print(L"class=%d type=%d data=%d machine=%d\n", 
231                 ehdr->e_ident[EI_CLASS],
232                 type,
233                 ehdr->e_ident[EI_DATA],
234                 machine));
235
236         return    ehdr->e_ident[EI_MAG0]  == 0x7f 
237                && ehdr->e_ident[EI_MAG1]  == 'E'
238                && ehdr->e_ident[EI_MAG2]  == 'L'
239                && ehdr->e_ident[EI_MAG3]  == 'F'
240                && ehdr->e_ident[EI_CLASS] == ELFCLASS32 
241                && type                    == ET_EXEC    /* must be executable */
242                && machine                 == EM_386 ? 0 : -1;
243 }
244
245 /*
246  * will invalidate loadble segments which overlap with others
247  */ 
248 void
249 check_overlap(int i)
250 {
251         int j;
252         unsigned long iend = chunks[i].addr + chunks[i].size;
253
254         for(j=0; j < nchunks; j++) {
255                 if (j ==i) continue;
256                 if (chunks[i].addr >= chunks[j].addr && iend < (chunks[j].addr + chunks[j].size)) {
257                         DBG_PRT((L"%s : segment %d fully included in segment %d\n", LD_NAME, i, j));
258                         CHUNK_INVALIDATE(i); /* nullyify segment */
259                         break;
260                 }
261         }
262 }
263
264 void
265 analyze_chunks(void)
266 {
267         INTN i;
268
269         for (i=0; i < nchunks; i++) {
270                 if (CHUNK_IS_VALID(i) && !CHUNK_IS_LOAD(i)) 
271                         check_overlap(i);
272         }
273 }
274
275
276 /*
277  * The decompression code calls this function after decompressing the
278  * first block of the object file.  The first block must contain all
279  * the relevant header information.
280  */
281 int
282 first_block (const unsigned char *buf, long blocksize)
283 {
284         Elf32_Ehdr *elf;
285         Elf32_Phdr *phdrs;
286         UINTN total_size, pages;
287         UINTN low_addr, max_addr;
288         UINTN offs = 0;
289         UINT16 phnum;
290         UINTN paddr, memsz;
291         INTN i;
292
293         elf  = (Elf32_Ehdr *)buf;
294         
295         if (is_valid_header(elf) == -1) 
296                 return -1;
297
298         offs  = elf->e_phoff;
299         phnum = elf->e_phnum;
300
301         VERB_PRT(3, { 
302                 Print(L"Entry point "PTR_FMT"\n", elf->e_entry);
303                 Print(L"%d program headers\n", phnum);
304                 Print(L"%d segment headers\n", elf->e_shnum);
305         });
306
307         if (offs + phnum * sizeof(*phdrs) > (unsigned) blocksize) {
308                 ERR_PRT((L"%s : ELF program headers not in first block (%d)\n", LD_NAME, offs));
309                 return -1;
310         }
311
312         kernel_entry = (VOID *)(elf->e_entry & PADDR_MASK);
313
314         phdrs = (Elf32_Phdr *) (buf + offs);
315         low_addr = ~0;
316         max_addr = 0;
317
318         /*
319          * allocate chunk table
320          * Convention: a segment that does not need loading will
321          * have chunk[].addr = 0.
322          */
323         chunks = (void *)alloc(sizeof(struct segment)*phnum, 0);
324         if (chunks == NULL) {
325                 ERR_PRT((L"%s : failed alloc chunks %r\n", LD_NAME));
326                 return -1;
327         }
328         nchunks = phnum;
329         /*
330          * find lowest and higest virtual addresses
331          * don't assume FULLY sorted !
332          */
333         for (i = 0; i < phnum; ++i) {
334                 /* 
335                  * record chunk no matter what because no load may happen
336                  * anywhere in archive, not just as the last segment
337                  */
338                 paddr = (phdrs[i].p_paddr & PADDR_MASK);
339                 memsz = phdrs[i].p_memsz,
340
341                 chunks[i].addr   = paddr;
342                 chunks[i].offset = phdrs[i].p_offset;
343                 chunks[i].size   = phdrs[i].p_filesz;
344                 chunks[i].bss_sz = phdrs[i].p_memsz - phdrs[i].p_filesz;
345
346                 CHUNK_VALIDATE(i);
347
348                 if (phdrs[i].p_type != PT_LOAD) {
349                         CHUNK_NO_LOAD(i); /* mark no load chunk */
350                         DBG_PRT((L"%s : skipping segment %d\n", LD_NAME, i));
351                         continue;
352                 }
353
354                 CHUNK_CAN_LOAD(i); /* mark no load chunk */
355
356                 VERB_PRT(3, 
357                 Print(L"\n%s : segment %d vaddr ["PTR_FMT"-"PTR_FMT"] offset %d filesz %d "
358                         "memsz=%d bss_sz=%d\n",
359                         LD_NAME, 1+i, chunks[i].addr, chunks[i].addr+phdrs[i].p_filesz, 
360                         chunks[i].offset, chunks[i].size, memsz, chunks[i].bss_sz));
361                 
362                 if (paddr < low_addr) 
363                         low_addr = paddr;
364                 if (paddr + memsz > max_addr) 
365                         max_addr = paddr + memsz;
366         }
367
368         if (low_addr & (EFI_PAGE_SIZE - 1)) {
369                 ERR_PRT((L"%s : low_addr not page aligned "PTR_FMT"\n", LD_NAME, low_addr));
370                 goto error;
371         }
372         analyze_chunks();
373
374         DBG_PRT((L"%s : %d program headers entry=" PTR_FMT "\nlowest_addr="PTR_FMT" highest_addr="PTR_FMT"\n", 
375                         LD_NAME,
376                         phnum, kernel_entry, low_addr, max_addr));
377
378         total_size = (UINTN)max_addr - (UINTN)low_addr;
379         pages = EFI_SIZE_TO_PAGES(total_size);
380
381         /*
382          * Record end of kernel for initrd
383          */
384         kernel_base = (void *)low_addr;
385         kernel_end  = (void *)(low_addr + (pages << EFI_PAGE_SHIFT));
386
387         /* allocate memory for the kernel */
388         if (alloc_kmem((void *)low_addr, pages) == -1) {
389                 ERR_PRT((L"%s : AllocatePages(%d, "PTR_FMT") for kernel failed\n", 
390                         LD_NAME, pages, low_addr));
391                 ERR_PRT((L"%s : Could not load kernel at "PTR_FMT"\n", LD_NAME, low_addr));
392                 ERR_PRT((L"%s : Bailing\n", LD_NAME));
393                 goto error;
394         }
395         return 0;
396 error:
397         if (chunks) 
398                 free(chunks);
399         return -1;
400 }
401
402 /*
403  * Determine which chunk in the Elf file will be coming out of the expand
404  * code next.
405  */
406 static void
407 nextchunk(void)
408 {
409         int i;
410         segment_t *cp;
411
412         cp = NULL;
413         for(i=0; i < nchunks; i++) {
414
415                 if (!CHUNK_IS_VALID(i) || !CHUNK_IS_LOAD(i)) continue;
416
417                 if (file_offset > chunks[i].offset) continue;
418
419                 if (cp == NULL || chunks[i].offset < cp->offset) cp = &chunks[i];
420         }
421         cur_chunk = cp;
422 }
423
424
425 /*
426  * Write the output window window[0..outcnt-1] holding uncompressed
427  * data and update crc.
428  */
429 void
430 flush_window(void)
431 {
432         static const CHAR8 helicopter[4] = { '|' , '/' , '-' , '\\' };
433         static UINTN heli_count;
434         struct segment *cp;
435         unsigned char   *src, *dst;
436         long    cnt;
437
438         if (!outcnt) return;
439 #ifdef DEBUG_GZIP
440         DBG_PRT((L"%s : flush_window outnct=%d file_offset=%d\n", LD_NAME, outcnt, file_offset));
441 #endif
442
443         Print(L"%c\b",helicopter[heli_count++%4]);
444
445         updcrc(window, outcnt);
446
447         /* first time, we extract the headers */
448         if (!bytes_out) {
449                 if (first_block(window, outcnt) < 0) 
450                         error("invalid exec header"); 
451                 nextchunk();
452         }
453
454         bytes_out += outcnt;
455         src = window;
456 tail:
457         /* check if user wants to abort */
458         if (check_abort() == EFI_SUCCESS) goto load_abort;
459
460         cp = cur_chunk;
461         if (cp == NULL || file_offset + outcnt <= cp->offset) {
462                 file_offset += outcnt;
463                 return;
464         }
465
466         /* Does this window begin before the current chunk? */
467         if (file_offset < cp->offset) {
468                 unsigned long skip = cp->offset - file_offset;
469
470                 src         += skip;
471                 file_offset += skip;
472                 outcnt      -= skip;
473         }
474         dst = (unsigned char *)cp->addr + (file_offset - cp->offset);
475         cnt = cp->offset + cp->size - file_offset;
476         if (cnt > outcnt) 
477                 cnt = outcnt;
478
479         Memcpy(dst, src, cnt);
480
481         file_offset += cnt;
482         outcnt      -= cnt;
483         src         += cnt;
484
485         /* See if we are at the end of this chunk */
486         if (file_offset == cp->offset + cp->size) {
487                 if (cp->bss_sz) {
488                         dst = (unsigned char *)cp->addr + cp->size;
489                         Memset(dst, 0, cp->bss_sz);
490                 }
491                 nextchunk();
492                 /* handle remaining bytes */
493                 if (outcnt) 
494                         goto tail; 
495         }
496         return;
497 load_abort:
498         free_kmem();
499         error_return = ELILO_LOAD_ABORTED;
500 }
501
502 static void
503 error(char *x)
504 {
505         ERR_PRT((L"%s : %a", LD_NAME, x));
506         /* will eventually exit with error from gunzip() */
507 }
508
509 INT32
510 decompress_kernel(VOID)
511 {
512         INT32 ret;
513
514         clear_bufs();
515         makecrc();
516         Print(L"Uncompressing Linux... ");
517         ret = gunzip();
518         if (ret == 0) 
519                 Print(L"done\n");
520         return ret == 0 ? 0 : -1;
521 }
522
523 int
524 gunzip_kernel(fops_fd_t fd, kdesc_t *kd)
525 {
526         int ret = -1;
527
528         error_return = ELILO_LOAD_ERROR;
529         
530         window = (void *)alloc(WSIZE, 0);
531         if (window == NULL) {
532                 ERR_PRT((L"%s : allocate output window failed\n", LD_NAME));
533                 return -1;
534         }
535
536         inbuf = (void *)alloc(INBUFSIZE, 0);
537         if (inbuf == NULL) {
538                 ERR_PRT((L"%s : allocate input window failedr\n", LD_NAME));
539                 goto error;
540         }
541         input_fd   = fd;
542         insize     = 0;
543         bytes_out  = 0;
544
545         ret = decompress_kernel();
546 error:
547         if (window) free(window);
548         if (inbuf) free(inbuf);
549
550         if (ret == 0) {
551                 kd->kentry = kernel_entry;
552                 kd->kend   = kernel_end;
553                 kd->kstart = kernel_base;
554                 error_return = ELILO_LOAD_SUCCESS;
555         }
556         return error_return;
557 }