Imported Upstream version 3.12
[debian/elilo] / ia64 / gzip.c
1 /*
2  *  Copyright (C) 2001-2003 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
35 #include "gzip.h"
36
37 #include "private.h"
38 #include "setjmp.h"
39
40 #define LD_NAME L"gzip_ia64"
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 #define CHUNK_FL_X              0x4
74
75 #define CHUNK_CAN_LOAD(n)       chunks[(n)].flags |= CHUNK_FL_LOAD
76 #define CHUNK_NO_LOAD(n)        chunks[(n)].flags &= ~CHUNK_FL_LOAD
77 #define CHUNK_IS_LOAD(n)        (chunks[(n)].flags & CHUNK_FL_LOAD)
78
79 #define CHUNK_VALIDATE(n)       chunks[(n)].flags |= CHUNK_FL_VALID
80 #define CHUNK_INVALIDATE(n)     chunks[(n)].flags = 0
81 #define CHUNK_IS_VALID(n)       (chunks[(n)].flags & CHUNK_FL_VALID)
82
83 /*
84  * static parameters to gzip helper functions
85  * we cannot use paramters because API was not
86  * designed that way
87  */
88 static segment_t *chunks;       /* holds the list of segments */
89 static segment_t *cur_chunk;
90 static UINTN nchunks;
91 static UINTN chunk;                 /* current segment */
92 static UINTN input_fd;
93 static VOID *kernel_entry, *kernel_base, *kernel_end;
94
95 static uch *inbuf;              /* input buffer (compressed data) */
96 static uch *window;             /* output buffer (uncompressed data) */
97 static unsigned long file_offset;       /* position in the file */
98
99 static unsigned insize = 0;  /* valid bytes in inbuf */
100 static unsigned inptr  = 0;   /* index of next byte to be processed in inbuf */
101 static unsigned outcnt = 0;  /* bytes in output buffer */
102
103 /* gzip flag byte */
104 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
105 #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
106 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
107 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
108 #define COMMENT      0x10 /* bit 4 set: file comment present */
109 #define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
110 #define RESERVED     0xC0 /* bit 6,7:   reserved */
111
112 #define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
113
114 /* Diagnostic functions */
115 #ifdef INFLATE_DEBUG
116 #  define Assert(cond,msg) {if(!(cond)) error(msg);}
117 int stderr;
118 #  define Trace(x) Print(L"line %d:\n", __LINE__);
119 #  define Tracev(x) {if (verbose) Print(L"line %d:\n", __LINE__) ;}
120 #  define Tracevv(x) {if (verbose>1) Print(L"line %d:\n", __LINE__)  ;}
121 #  define Tracec(c,x) {if (verbose && (c))  Print(L"line %d:\n", __LINE__) ;}
122 #  define Tracecv(c,x) {if (verbose>1 && (c))  Print(L"line %d:\n", __LINE__) ;}
123 #else
124 #  define Assert(cond,msg)
125 #  define Trace(x)
126 #  define Tracev(x)
127 #  define Tracevv(x)
128 #  define Tracec(c,x)
129 #  define Tracecv(c,x)
130 #endif
131
132 static int  fill_inbuf(void);
133 static void flush_window(void);
134 static void error(char *m);
135 static long bytes_out;
136
137 static void error(char *m);
138
139 static jmp_buf jbuf;
140 static int error_return;
141 static UINTN elf_is_big_endian; /* true if ELF file is big endian */
142
143 static void *
144 gzip_malloc(int size)
145 {
146         return (void *)alloc(size, 0);
147 }
148
149 static void
150 gzip_free(void *where)
151 {       
152         return free(where);
153 }
154
155 #include "inflate.c"
156
157 /*
158  * Fill the input buffer and return the first byte in it. This is called
159  * only when the buffer is empty and at least one byte is really needed.
160  */
161 int
162 fill_inbuf(void)
163 {
164         UINTN expected, nread;
165         EFI_STATUS status;
166
167         expected = nread = INBUFSIZE;
168
169         status = fops_read(input_fd, inbuf, &nread);
170         if (EFI_ERROR(status)) {
171                 error("elilo: Read failed");
172         }
173 #ifdef DEBUG_GZIP
174         DBG_PRT((L"%s : read %d bytes of %d bytes\n", LD_NAME, nread, expected));
175 #endif
176
177         insize = nread;
178         inptr = 1;
179
180         return inbuf[0];
181 }
182
183 /* ===========================================================================
184  * Write the output window window[0..outcnt-1] and update crc and bytes_out.
185  * (Used for the decompressed data only.)
186  */
187
188 /*
189  * Run a set of bytes through the crc shift register.  If s is a NULL
190  * pointer, then initialize the crc shift register contents instead.
191  * Return the current crc in either case.
192  *
193  * Input:
194  *      S       pointer to bytes to pump through.
195  *      N       number of bytes in S[].
196  */
197 unsigned long
198 updcrc(unsigned char *s, unsigned n)
199 {
200         register unsigned long c;
201         /* crc is defined in inflate.c */
202
203         if (!s) {
204                 c = 0xffffffffL;
205         } else {
206                 c = crc;
207                 while (n--) {
208                         c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
209                 }
210         }
211         crc = c;
212         return c ^ 0xffffffffUL;       /* (instead of ~c for 64-bit machines) */
213 }
214
215
216 /*
217  * Clear input and output buffers
218  */
219 void
220 clear_bufs(void)
221 {
222         outcnt = 0;
223         inptr = 0;
224         chunk = 0;
225         cur_chunk = NULL;
226         file_offset = 0;
227 }
228
229
230 static inline UINT64 
231 bswap64(UINT64 v)
232 {
233         if(elf_is_big_endian) v = __ia64_swab64(v);
234         return v;
235 }
236
237 static inline UINT32
238 bswap32(UINT32 v)
239 {
240         if(elf_is_big_endian) v = __ia64_swab32(v);
241         return v;
242 }
243
244 static inline UINT16
245 bswap16(UINT16 v)
246 {
247         if(elf_is_big_endian) v = __ia64_swab16(v);
248         return v;
249 }
250
251 static INTN
252 is_valid_header(Elf64_Ehdr *ehdr)
253 {
254         UINT16 type, machine;
255
256         if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
257                 type    = __ia64_swab16(ehdr->e_type);
258                 machine = __ia64_swab16(ehdr->e_machine);
259         } else {
260                 type    = ehdr->e_type;
261                 machine = ehdr->e_machine;
262         }
263         VERB_PRT(3, Print(L"class=%d type=%d data=%d machine=%d\n", 
264                 ehdr->e_ident[EI_CLASS],
265                 type,
266                 ehdr->e_ident[EI_DATA],
267                 machine));
268
269         return    ehdr->e_ident[EI_MAG0]  == 0x7f 
270                && ehdr->e_ident[EI_MAG1]  == 'E'
271                && ehdr->e_ident[EI_MAG2]  == 'L'
272                && ehdr->e_ident[EI_MAG3]  == 'F'
273                && ehdr->e_ident[EI_CLASS] == ELFCLASS64 
274                && type                    == ET_EXEC    /* must be executable */
275                && machine                 == EM_IA_64 ? 0 : -1;
276 }
277
278 /*
279  * will invalidate loadble segments which overlap with others
280  */ 
281 void
282 check_overlap(int i)
283 {
284         int j;
285         unsigned long iend = chunks[i].addr + chunks[i].size;
286
287         for(j=0; j < nchunks; j++) {
288                 if (j ==i) continue;
289                 if (chunks[i].addr >= chunks[j].addr && iend < (chunks[j].addr + chunks[j].size)) {
290                         DBG_PRT((L"%s : segment %d fully included in segment %d\n", LD_NAME, i, j));
291                         CHUNK_INVALIDATE(i); /* nullyify segment */
292                         break;
293                 }
294         }
295 }
296
297 void
298 analyze_chunks(void)
299 {
300         INTN i;
301
302         for(i=0; i < nchunks; i++) {
303                 if (CHUNK_IS_VALID(i) && !CHUNK_IS_LOAD(i)) check_overlap(i);
304         }
305 }
306
307
308 /*
309  * The decompression code calls this function after decompressing the
310  * first block of the object file.  The first block must contain all
311  * the relevant header information.
312  */
313 int
314 first_block (const unsigned char *buf, long blocksize)
315 {
316         Elf64_Ehdr *elf;
317         Elf64_Phdr *phdrs;
318         UINTN total_size, pages;
319         UINTN low_addr, max_addr;
320         UINTN load_offset = 0;
321         UINTN offs = 0;
322         UINT16 phnum;
323         UINTN paddr, memsz;
324         INTN i;
325
326         elf  = (Elf64_Ehdr *)buf;
327         
328         if (is_valid_header(elf) == -1) return -1;
329
330         /* determine file endianess */
331         elf_is_big_endian = elf->e_ident[EI_DATA] == ELFDATA2MSB ? 1 : 0;
332
333         
334         offs  = bswap64(elf->e_phoff);
335         phnum = bswap16(elf->e_phnum);
336
337         VERB_PRT(3, { 
338                         Print(L"ELF file is %s\n", elf_is_big_endian ? L"big endian" : L"little endian");
339                         Print(L"Entry point 0x%lx\n", bswap64(elf->e_entry));
340                         Print(L"%d program headers\n", phnum);
341                         Print(L"%d segment headers\n", bswap16(elf->e_shnum));
342                    });
343
344
345         /* XXX: need to check on this */
346         if (offs + phnum * sizeof(*phdrs) > (unsigned) blocksize) {
347                 ERR_PRT((L"%s : ELF program headers not in first block (%ld)\n", LD_NAME, offs));
348                 return -1;
349         }
350
351         kernel_entry = (void *)bswap64(elf->e_entry);
352
353         if (((UINTN)kernel_entry >> 61) != 0) {
354                 ERR_PRT((L"%s:  <<ERROR>> entry point is a virtual address 0x%lx : not supported anymore\n", LD_NAME, kernel_entry));
355         }
356
357         phdrs = (Elf64_Phdr *) (buf + offs);
358
359         low_addr = ~0;
360         max_addr = 0;
361
362         /*
363          * allocate chunk table
364          * Convention: a segment that does not need loading will
365          * have chunk[].addr = 0.
366          */
367         chunks = (void *)alloc(sizeof(struct segment)*phnum, 0);
368         if (chunks == NULL) {
369                 ERR_PRT((L"%s : failed alloc chunks %r\n", LD_NAME));
370                 return -1;
371         }
372         nchunks = phnum;
373         /*
374          * find lowest and higest virtual addresses
375          * don't assume FULLY sorted !
376          */
377         for (i = 0; i < phnum; ++i) {
378
379                 /* 
380                  * record chunk no matter what because no load may happen
381                  * anywhere in archive, not just as the last segment
382                  */
383                 paddr = bswap64(phdrs[i].p_paddr);
384                 memsz = bswap64(phdrs[i].p_memsz),
385
386                 chunks[i].addr   = paddr;
387                 chunks[i].offset = bswap64(phdrs[i].p_offset);
388                 chunks[i].size   = bswap64(phdrs[i].p_filesz);
389                 chunks[i].bss_sz = bswap64(phdrs[i].p_memsz) - bswap64(phdrs[i].p_filesz);
390
391                 CHUNK_VALIDATE(i);
392
393                 if (bswap32(phdrs[i].p_type) != PT_LOAD) {
394                         CHUNK_NO_LOAD(i); /* mark no load chunk */
395                         DBG_PRT((L"%s : skipping segment %ld\n", LD_NAME, i));
396                         continue;
397                 }
398
399                 if (bswap32(phdrs[i].p_flags) & PF_X)
400                         chunks[i].flags |= CHUNK_FL_X;
401
402                 CHUNK_CAN_LOAD(i); /* mark no load chunk */
403
404                 VERB_PRT(3, 
405                 Print(L"\n%s : segment %ld vaddr [0x%lx-0x%lx] offset %ld filesz %ld memsz=%ld bss_sz=%ld\n",
406                                 LD_NAME,
407                                 1+i, 
408                                 chunks[i].addr, 
409                                 chunks[i].addr+bswap64(phdrs[i].p_filesz), 
410                                 chunks[i].offset, 
411                                 chunks[i].size,
412                                 memsz,
413                                 chunks[i].bss_sz));
414                 
415                 if (paddr < low_addr) low_addr = paddr;
416
417                 if (paddr + memsz > max_addr) max_addr = paddr + memsz;
418         }
419
420         if (low_addr & (EFI_PAGE_SIZE - 1)) {
421                 ERR_PRT((L"%s : low_addr not page aligned 0x%lx\n", LD_NAME, low_addr));
422                 goto error;
423         }
424
425         analyze_chunks();
426
427         DBG_PRT((L"%s : %d program headers entry=0x%lx\nlowest_addr=0x%lx highest_addr=0x%lx\n", 
428                         LD_NAME,
429                         phnum, kernel_entry, low_addr, max_addr));
430
431         total_size = (UINTN)max_addr - (UINTN)low_addr;
432         pages = EFI_SIZE_TO_PAGES(total_size);
433
434         /*
435          * Record end of kernel for initrd
436          */
437         kernel_base = (void *)low_addr;
438         kernel_end  = (void *)(low_addr + (pages << EFI_PAGE_SHIFT));
439
440         /* allocate memory for the kernel */
441         if (alloc_kmem((void *)low_addr, pages) == -1) {
442                 VOID *new_addr;
443
444                 VERB_PRT(1, Print(L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr));
445
446                 if (ia64_can_relocate() == 0) {
447                         ERR_PRT((L"relocation is disabled, cannot load kernel"));
448                         goto error;
449                 }
450
451                 /*
452                  * could not allocate at requested spot, try to find a
453                  * suitable location to relocate the kernel
454                  *
455                  * The maximum sized Itanium TLB translation entry is 256 MB.
456                  * If we relocate the kernel by this amount we know for sure
457                  * that alignment constraints will be satisified, regardless
458                  * of the kernel used.
459                  */
460                 VERB_PRT(1, Print(L"Attempting to relocate kernel.\n"));
461
462                 if (find_kernel_memory((VOID*) low_addr, (VOID*) max_addr, 256*MB, &new_addr) == -1) {
463                         ERR_PRT((L"%s : find_kernel_memory(0x%lx, 0x%lx, 0x%lx, 0x%lx) failed\n", LD_NAME, low_addr, max_addr, 256*MB, &load_offset));
464                         goto error;
465                 }
466                 /* unsigned arithmetic */
467                 load_offset = (UINTN) (new_addr - ROUNDDOWN((UINTN) low_addr,256*MB));
468
469                 VERB_PRT(1, Print(L"low_addr=0x%lx new_addr=0x%lx offset=0x%lx", low_addr, new_addr, load_offset));
470
471                 /*
472                  * correct various addresses for non-zero load_offset
473                  */
474                 kernel_base = (void *) ((UINTN) kernel_base + load_offset);
475                 kernel_end  = (void *) ((UINTN) kernel_end + load_offset);
476                 kernel_entry = (void*) ((UINTN) kernel_entry + load_offset);
477
478                 for (i = 0; i < phnum; ++i) {
479                         chunks[i].addr += load_offset;
480                         phdrs[i].p_paddr = (Elf64_Addr) ((UINT64) phdrs[i].p_paddr + load_offset);
481                 }
482
483                 /*
484                  * try one last time to get memory for the kernel
485                  */
486                 if (alloc_kmem((void *)low_addr+load_offset, pages) == -1) {
487                         ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", LD_NAME, pages, low_addr+load_offset));
488                         ERR_PRT((L"Relocation by 0x%lx bytes failed.\n", load_offset));
489                         goto error;
490                 }
491         }
492         return 0;
493 error:
494         if (chunks) free(chunks);
495         return -1;
496 }
497
498 /*
499  * Determine which chunk in the Elf file will be coming out of the expand
500  * code next.
501  */
502 static void
503 nextchunk(void)
504 {
505         int i;
506         segment_t *cp;
507
508         cp = NULL;
509         for(i=0; i < nchunks; i++) {
510
511                 if (!CHUNK_IS_VALID(i) || !CHUNK_IS_LOAD(i)) continue;
512
513                 if (file_offset > chunks[i].offset) continue;
514
515                 if (cp == NULL || chunks[i].offset < cp->offset) cp = &chunks[i];
516         }
517         cur_chunk = cp;
518 }
519
520
521 /*
522  * Write the output window window[0..outcnt-1] holding uncompressed
523  * data and update crc.
524  */
525 void
526 flush_window(void)
527 {
528         static const CHAR8 helicopter[4] = { '|' , '/' , '-' , '\\' };
529         static UINTN heli_count;
530         struct segment *cp;
531         unsigned char   *src, *dst;
532         long    cnt;
533
534         if (!outcnt) return;
535 #ifdef DEBUG_GZIP
536         DBG_PRT((L"%s : flush_window outnct=%d file_offset=%ld\n", LD_NAME, outcnt, file_offset));
537 #endif
538
539         Print(L"%c\b",helicopter[heli_count++%4]);
540
541         updcrc(window, outcnt);
542
543         /*
544          * first time, we extract the headers
545          */
546         if (!bytes_out) {
547                 if (first_block(window, outcnt) < 0) error("invalid exec header"); 
548                 nextchunk();
549         }
550
551         bytes_out += outcnt;
552         src = window;
553 tail:
554         /* check if user wants to abort */
555         if (check_abort() == EFI_SUCCESS) goto load_abort;
556
557         cp = cur_chunk;
558         if (cp == NULL || file_offset + outcnt <= cp->offset) {
559                 file_offset += outcnt;
560                 return;
561         }
562
563         // Does this window begin before the current chunk?
564         if (file_offset < cp->offset) {
565                 unsigned long skip = cp->offset - file_offset;
566
567                 src         += skip;
568                 file_offset += skip;
569                 outcnt      -= skip;
570         }
571         dst = (unsigned char *)cp->addr + (file_offset - cp->offset);
572
573         cnt = cp->offset + cp->size - file_offset;
574
575         if (cnt > outcnt) cnt = outcnt;
576
577         Memcpy(dst, src, cnt);
578         if (cp->flags & CHUNK_FL_X)
579                 flush_dcache (dst, cnt);
580
581         file_offset += cnt;
582         outcnt      -= cnt;
583         src         += cnt;
584
585         /* See if we are at the end of this chunk */
586         if (file_offset == cp->offset + cp->size) {
587                 if (cp->bss_sz) {
588                         dst = (unsigned char *)cp->addr + cp->size;
589                         Memset(dst, 0, cp->bss_sz);
590                 }
591                 nextchunk();
592                 /* handle remaining bytes */
593                 if (outcnt) goto tail; 
594         }
595         return;
596 load_abort:
597         free_kmem();
598         error_return = ELILO_LOAD_ABORTED;
599         longjmp(jbuf, 1);
600 }
601
602 static void
603 error(char *x)
604 {
605         ERR_PRT((L"%s : %a", LD_NAME, x));
606         /* will eventually exit with error from gunzip() */
607         longjmp(jbuf,1);
608 }
609
610 INT32
611 decompress_kernel(VOID)
612 {
613         INT32 ret;
614
615         clear_bufs();
616         makecrc();
617         Print(L"Uncompressing Linux... ");
618         ret = gunzip();
619         if (ret == 0) Print(L"done\n");
620         return ret == 0 ? 0 : -1;
621 }
622
623 int
624 gunzip_kernel(fops_fd_t fd, kdesc_t *kd)
625 {
626         int ret = -1;
627
628         error_return = ELILO_LOAD_ERROR;
629         
630         window = (void *)alloc(WSIZE, 0);
631         if (window == NULL) {
632                 ERR_PRT((L"%s : allocate output window failed\n", LD_NAME));
633                 return -1;
634         }
635
636         inbuf = (void *)alloc(INBUFSIZE, 0);
637         if (inbuf == NULL) {
638                 ERR_PRT((L"%s : allocate input window failedr\n", LD_NAME));
639                 goto error;
640         }
641
642         input_fd   = fd;
643         insize     = 0;
644         bytes_out  = 0;
645
646         if (setjmp(jbuf) == 1) goto error;
647
648
649         ret = decompress_kernel();
650
651 error:
652         if (window) free(window);
653         if (inbuf) free(inbuf);
654
655         if (ret == 0) {
656                 kd->kentry = kernel_entry;
657                 kd->kend   = kernel_end;
658                 kd->kstart = kernel_base;
659                 error_return = ELILO_LOAD_SUCCESS;
660         }
661         return error_return;
662 }