2 * Copyright (C) 2001-2002 Hewlett-Packard Co.
3 * Contributed by Stephane Eranian <eranian@hpl.hp.com>
5 * Copyright (C) 2001 Silicon Graphics, Inc.
6 * Contributed by Brent Casavant <bcasavan@sgi.com>
8 * This file is part of the ELILO, the EFI Linux boot loader.
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)
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.
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
25 * Please check out the elilo.txt for complete documentation on how
26 * to use this program.
37 #define LD_NAME L"gzip_ia32"
39 #define memzero(s, n) Memset((VOID *)(s), 0, (n))
40 #define memcpy(a,b,n) Memcpy((VOID *)(a),(b),(n))
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
53 #define FUNC_STATIC static
55 typedef unsigned char uch;
56 typedef unsigned short ush;
57 typedef unsigned long ulg;
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 */
68 #define CHUNK_FL_VALID 0x1
69 #define CHUNK_FL_LOAD 0x2
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)
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)
80 * static parameters to gzip helper functions
81 * we cannot use paramters because API was not
84 static segment_t *chunks; /* holds the list of segments */
85 static segment_t *cur_chunk;
87 static UINTN chunk; /* current segment */
88 static UINTN input_fd;
89 static VOID *kernel_entry, *kernel_base, *kernel_end;
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 */
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 */
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 */
108 #define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
110 /* Diagnostic functions */
112 # define Assert(cond,msg) {if(!(cond)) error(msg);}
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__) ;}
120 # define Assert(cond,msg)
125 # define Tracecv(c,x)
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;
136 gzip_malloc(int size)
138 return (void *)alloc(size, 0);
142 gzip_free(void *where)
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.
156 UINTN expected, nread;
159 expected = nread = INBUFSIZE;
161 status = fops_read(input_fd, inbuf, &nread);
162 if (EFI_ERROR(status)) {
163 error("elilo: Read failed");
166 DBG_PRT((L"%s : read %d bytes of %d bytes\n", LD_NAME, nread, expected));
175 /* ===========================================================================
176 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
177 * (Used for the decompressed data only.)
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.
186 * S pointer to bytes to pump through.
187 * N number of bytes in S[].
190 updcrc(unsigned char *s, unsigned n)
192 register unsigned long c;
193 /* crc is defined in inflate.c */
200 c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
204 return c ^ 0xffffffffUL; /* (instead of ~c for 64-bit machines) */
209 * Clear input and output buffers
223 is_valid_header(Elf32_Ehdr *ehdr)
225 UINT16 type, machine;
228 machine = ehdr->e_machine;
230 VERB_PRT(3, Print(L"class=%d type=%d data=%d machine=%d\n",
231 ehdr->e_ident[EI_CLASS],
233 ehdr->e_ident[EI_DATA],
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;
246 * will invalidate loadble segments which overlap with others
252 unsigned long iend = chunks[i].addr + chunks[i].size;
254 for(j=0; j < nchunks; j++) {
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 */
269 for (i=0; i < nchunks; i++) {
270 if (CHUNK_IS_VALID(i) && !CHUNK_IS_LOAD(i))
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.
282 first_block (const unsigned char *buf, long blocksize)
286 UINTN total_size, pages;
287 UINTN low_addr, max_addr;
293 elf = (Elf32_Ehdr *)buf;
295 if (is_valid_header(elf) == -1)
299 phnum = elf->e_phnum;
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);
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));
312 kernel_entry = (VOID *)(elf->e_entry & PADDR_MASK);
314 phdrs = (Elf32_Phdr *) (buf + offs);
319 * allocate chunk table
320 * Convention: a segment that does not need loading will
321 * have chunk[].addr = 0.
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));
330 * find lowest and higest virtual addresses
331 * don't assume FULLY sorted !
333 for (i = 0; i < phnum; ++i) {
335 * record chunk no matter what because no load may happen
336 * anywhere in archive, not just as the last segment
338 paddr = (phdrs[i].p_paddr & PADDR_MASK);
339 memsz = phdrs[i].p_memsz,
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;
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));
354 CHUNK_CAN_LOAD(i); /* mark no load chunk */
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));
362 if (paddr < low_addr)
364 if (paddr + memsz > max_addr)
365 max_addr = paddr + memsz;
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));
374 DBG_PRT((L"%s : %d program headers entry=" PTR_FMT "\nlowest_addr="PTR_FMT" highest_addr="PTR_FMT"\n",
376 phnum, kernel_entry, low_addr, max_addr));
378 total_size = (UINTN)max_addr - (UINTN)low_addr;
379 pages = EFI_SIZE_TO_PAGES(total_size);
382 * Record end of kernel for initrd
384 kernel_base = (void *)low_addr;
385 kernel_end = (void *)(low_addr + (pages << EFI_PAGE_SHIFT));
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));
403 * Determine which chunk in the Elf file will be coming out of the expand
413 for(i=0; i < nchunks; i++) {
415 if (!CHUNK_IS_VALID(i) || !CHUNK_IS_LOAD(i)) continue;
417 if (file_offset > chunks[i].offset) continue;
419 if (cp == NULL || chunks[i].offset < cp->offset) cp = &chunks[i];
426 * Write the output window window[0..outcnt-1] holding uncompressed
427 * data and update crc.
432 static const CHAR8 helicopter[4] = { '|' , '/' , '-' , '\\' };
433 static UINTN heli_count;
435 unsigned char *src, *dst;
440 DBG_PRT((L"%s : flush_window outnct=%d file_offset=%d\n", LD_NAME, outcnt, file_offset));
443 Print(L"%c\b",helicopter[heli_count++%4]);
445 updcrc(window, outcnt);
447 /* first time, we extract the headers */
449 if (first_block(window, outcnt) < 0)
450 error("invalid exec header");
457 /* check if user wants to abort */
458 if (check_abort() == EFI_SUCCESS) goto load_abort;
461 if (cp == NULL || file_offset + outcnt <= cp->offset) {
462 file_offset += outcnt;
466 /* Does this window begin before the current chunk? */
467 if (file_offset < cp->offset) {
468 unsigned long skip = cp->offset - file_offset;
474 dst = (unsigned char *)cp->addr + (file_offset - cp->offset);
475 cnt = cp->offset + cp->size - file_offset;
479 Memcpy(dst, src, cnt);
485 /* See if we are at the end of this chunk */
486 if (file_offset == cp->offset + cp->size) {
488 dst = (unsigned char *)cp->addr + cp->size;
489 Memset(dst, 0, cp->bss_sz);
492 /* handle remaining bytes */
499 error_return = ELILO_LOAD_ABORTED;
505 ERR_PRT((L"%s : %a", LD_NAME, x));
506 /* will eventually exit with error from gunzip() */
510 decompress_kernel(VOID)
516 Print(L"Uncompressing Linux... ");
520 return ret == 0 ? 0 : -1;
524 gunzip_kernel(fops_fd_t fd, kdesc_t *kd)
528 error_return = ELILO_LOAD_ERROR;
530 window = (void *)alloc(WSIZE, 0);
531 if (window == NULL) {
532 ERR_PRT((L"%s : allocate output window failed\n", LD_NAME));
536 inbuf = (void *)alloc(INBUFSIZE, 0);
538 ERR_PRT((L"%s : allocate input window failedr\n", LD_NAME));
545 ret = decompress_kernel();
547 if (window) free(window);
548 if (inbuf) free(inbuf);
551 kd->kentry = kernel_entry;
552 kd->kend = kernel_end;
553 kd->kstart = kernel_base;
554 error_return = ELILO_LOAD_SUCCESS;