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