gzip.c

来自「EFI(Extensible Firmware Interface)是下一代BI」· C语言 代码 · 共 553 行

C
553
字号
/* *  Copyright (C) 2001-2002 Hewlett-Packard Co. *	Contributed by Stephane Eranian <eranian@hpl.hp.com> * *  Copyright (C) 2001 Silicon Graphics, Inc. *      Contributed by Brent Casavant <bcasavan@sgi.com> * * This file is part of the ELILO, the EFI Linux boot loader. * *  ELILO is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2, or (at your option) *  any later version. * *  ELILO is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with ELILO; see the file COPYING.  If not, write to the Free *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA *  02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. */#include <efi.h>#include <efilib.h>#include "elf.h"#include "elilo.h"#include "gzip.h"#include "private.h"#define memzero(s, n)	Memset((VOID *)(s), 0, (n))#define memcpy(a,b,n)	Memcpy((VOID *)(a),(b),(n))/* size of output buffer */#define WSIZE 0x8000		/* Window size must be at least 32k, */				/* and a power of two *//* size of input buffer */#define INBUFSIZE 0x8000/* * gzip declarations */#define OF(args)  args#define FUNC_STATIC statictypedef unsigned char  uch;typedef unsigned short ush;typedef unsigned long  ulg;typedef struct segment {	unsigned long addr;	/* start address */       	unsigned long offset;	/* file offset   */       	unsigned long size;	/* file size     */	unsigned long bss_sz;	/* BSS size      */	UINT8	flags;	/* indicates whether to load or not */} segment_t;#define CHUNK_FL_VALID		0x1#define CHUNK_FL_LOAD		0x2#define CHUNK_CAN_LOAD(n)	chunks[(n)].flags |= CHUNK_FL_LOAD#define CHUNK_NO_LOAD(n)	chunks[(n)].flags &= ~CHUNK_FL_LOAD#define CHUNK_IS_LOAD(n)	(chunks[(n)].flags & CHUNK_FL_LOAD)#define CHUNK_VALIDATE(n)	chunks[(n)].flags |= CHUNK_FL_VALID#define CHUNK_INVALIDATE(n)	chunks[(n)].flags = 0#define CHUNK_IS_VALID(n)	(chunks[(n)].flags & CHUNK_FL_VALID)/* * static parameters to gzip helper functions * we cannot use paramters because API was not * designed that way */static segment_t *chunks;	/* holds the list of segments */static segment_t *cur_chunk;static UINTN nchunks;static UINTN chunk;                 /* current segment */static UINTN input_fd;static VOID *kernel_entry, *kernel_base, *kernel_end;static uch *inbuf;		/* input buffer (compressed data) */static uch *window;    		/* output buffer (uncompressed data) */static unsigned long file_offset;	/* position in the file */static unsigned insize = 0;  /* valid bytes in inbuf */static unsigned inptr  = 0;   /* index of next byte to be processed in inbuf */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 get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())/* Diagnostic functions */#ifdef INFLATE_DEBUG#  define Assert(cond,msg) {if(!(cond)) error(msg);}int stderr;#  define Trace(x) Print(L"line %d:\n", __LINE__);#  define Tracev(x) {if (verbose) Print(L"line %d:\n", __LINE__) ;}#  define Tracevv(x) {if (verbose>1) Print(L"line %d:\n", __LINE__)  ;}#  define Tracec(c,x) {if (verbose && (c))  Print(L"line %d:\n", __LINE__) ;}#  define Tracecv(c,x) {if (verbose>1 && (c))  Print(L"line %d:\n", __LINE__) ;}#else#  define Assert(cond,msg)#  define Trace(x)#  define Tracev(x)#  define Tracevv(x)#  define Tracec(c,x)#  define Tracecv(c,x)#endifstatic int  fill_inbuf(void);static void flush_window(void);static void error(char *m);static long bytes_out;static void error(char *m);static int error_return;static void *gzip_malloc(int size){	return (void *)alloc(size, 0);}static voidgzip_free(void *where){		return free(where);}#include "inflate.c"/* * Fill the input buffer and return the first byte in it. This is called * only when the buffer is empty and at least one byte is really needed. */intfill_inbuf(void){	INTN expected, nread;	EFI_STATUS status;	expected = nread = INBUFSIZE;	status = fops_read(input_fd, inbuf, &nread);	if (EFI_ERROR(status)) {    		error("elilo: Read failed");	}	DBG_PRT((L"%s : read %d bytes of %d bytes\n", LD_NAME, nread, expected));	insize = nread;	inptr = 1;	return inbuf[0];}/* =========================================================================== * Write the output window window[0..outcnt-1] and update crc and bytes_out. * (Used for the decompressed data only.) *//* * Run a set of bytes through the crc shift register.  If s is a NULL * pointer, then initialize the crc shift register contents instead. * Return the current crc in either case. * * Input: *	S	pointer to bytes to pump through. *	N	number of bytes in S[]. */unsigned longupdcrc(unsigned char *s, unsigned n){	register unsigned long c;	/* crc is defined in inflate.c */	if (!s) {		c = 0xffffffffL;	} else {		c = crc;		while (n--) {			c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);		}	}	crc = c;	return c ^ 0xffffffffUL;       /* (instead of ~c for 64-bit machines) */}/* * Clear input and output buffers */voidclear_bufs(void){	outcnt = 0;	inptr = 0;	chunk = 0;	cur_chunk = NULL;	file_offset = 0;}static INTNis_valid_header(Elf32_Ehdr *ehdr){	UINT16 type, machine;	type    = ehdr->e_type;	machine = ehdr->e_machine;		VERB_PRT(3, Print(L"class=%d type=%d data=%d machine=%d\n", 		ehdr->e_ident[EI_CLASS],		type,		ehdr->e_ident[EI_DATA],		machine));	return    ehdr->e_ident[EI_MAG0]  == 0x7f 	       && ehdr->e_ident[EI_MAG1]  == 'E'	       && ehdr->e_ident[EI_MAG2]  == 'L'	       && ehdr->e_ident[EI_MAG3]  == 'F'	       && ehdr->e_ident[EI_CLASS] == ELFCLASS32 	       && type                    == ET_EXEC	/* must be executable */	       && machine                 == EM_386 ? 0 : -1;}/* * will invalidate loadble segments which overlap with others */ voidcheck_overlap(int i){	int j;	unsigned long iend = chunks[i].addr + chunks[i].size;	for(j=0; j < nchunks; j++) {		if (j ==i) continue;		if (chunks[i].addr >= chunks[j].addr && iend < (chunks[j].addr + chunks[j].size)) {			DBG_PRT((L"%s : segment %d fully included in segment %d\n", LD_NAME, i, j));			CHUNK_INVALIDATE(i); /* nullyify segment */			break;		}	}}voidanalyze_chunks(void){	INTN i;	for (i=0; i < nchunks; i++) {		if (CHUNK_IS_VALID(i) && !CHUNK_IS_LOAD(i)) 			check_overlap(i);	}}/* * The decompression code calls this function after decompressing the * first block of the object file.  The first block must contain all * the relevant header information. */intfirst_block (const char *buf, long blocksize){	Elf32_Ehdr *elf;	Elf32_Phdr *phdrs;	UINTN total_size, pages;	UINTN low_addr, max_addr;	UINTN offs = 0;	UINT16 phnum;	UINTN paddr, memsz;	INTN i;	elf  = (Elf32_Ehdr *)buf;		if (is_valid_header(elf) == -1) 		return -1;	offs  = elf->e_phoff;	phnum = elf->e_phnum;	VERB_PRT(3, { 		Print(L"Entry point 0x%lx\n", elf->e_entry);		Print(L"%d program headers\n", phnum);		Print(L"%d segment headers\n", elf->e_shnum);	});	if (offs + phnum * sizeof(*phdrs) > (unsigned) blocksize) {		ERR_PRT((L"%s : ELF program headers not in first block (%ld)\n", LD_NAME, offs));		return -1;	}	kernel_entry = (VOID *)(elf->e_entry & PADDR_MASK);	phdrs = (Elf32_Phdr *) (buf + offs);	low_addr = ~0;	max_addr = 0;	/*	 * allocate chunk table	 * Convention: a segment that does not need loading will	 * have chunk[].addr = 0.	 */	chunks = (void *)alloc(sizeof(struct segment)*phnum, 0);    	if (chunks == NULL) {        	ERR_PRT((L"%s : failed alloc chunks %r\n", LD_NAME));		return -1;    	}	nchunks = phnum;	/*	 * find lowest and higest virtual addresses	 * don't assume FULLY sorted !	 */	for (i = 0; i < phnum; ++i) {		/* 		 * record chunk no matter what because no load may happen		 * anywhere in archive, not just as the last segment		 */		paddr = (phdrs[i].p_paddr & PADDR_MASK);		memsz = phdrs[i].p_memsz,		chunks[i].addr   = paddr;		chunks[i].offset = phdrs[i].p_offset;		chunks[i].size   = phdrs[i].p_filesz;		chunks[i].bss_sz = phdrs[i].p_memsz - phdrs[i].p_filesz;		CHUNK_VALIDATE(i);		if (phdrs[i].p_type != PT_LOAD) {			CHUNK_NO_LOAD(i); /* mark no load chunk */			DBG_PRT((L"%s : skipping segment %ld\n", LD_NAME, i));			continue;		}		CHUNK_CAN_LOAD(i); /* mark no load chunk */		VERB_PRT(3, 		Print(L"\n%s : segment %ld vaddr [0x%lx-0x%lx] offset %ld filesz %ld "			"memsz=%ld bss_sz=%ld\n",			LD_NAME, 1+i, chunks[i].addr, chunks[i].addr+phdrs[i].p_filesz, 			chunks[i].offset, chunks[i].size, memsz, chunks[i].bss_sz));				if (paddr < low_addr) 			low_addr = paddr;		if (paddr + memsz > max_addr) 			max_addr = paddr + memsz;	}	if (low_addr & (EFI_PAGE_SIZE - 1)) {		ERR_PRT((L"%s : low_addr not page aligned 0x%lx\n", LD_NAME, low_addr));		goto error;	}	analyze_chunks();	DBG_PRT((L"%s : %d program headers entry=0x%lx\nlowest_addr=0x%lx highest_addr=0x%lx\n", 			LD_NAME,			phnum, kernel_entry, low_addr, max_addr));	total_size = (UINTN)max_addr - (UINTN)low_addr;	pages = EFI_SIZE_TO_PAGES(total_size);	/*	 * Record end of kernel for initrd	 */	kernel_base = (void *)low_addr;	kernel_end  = (void *)(low_addr + (pages << EFI_PAGE_SHIFT));	/* allocate memory for the kernel */	if (alloc_kmem((void *)low_addr, pages) == -1) {		ERR_PRT((L"%s : AllocatePages(%d, 0x%lx) for kernel failed\n", 			LD_NAME, pages, low_addr));		ERR_PRT((L"%s : Could not load kernel at 0x%lx\n", LD_NAME, low_addr));		ERR_PRT((L"%s : Bailing\n", LD_NAME));		goto error;	}	return 0;error:	if (chunks) 		free(chunks);	return -1;}/* * Determine which chunk in the Elf file will be coming out of the expand * code next. */static voidnextchunk(void){	int i;	segment_t *cp;	cp = NULL;	for(i=0; i < nchunks; i++) {		if (!CHUNK_IS_VALID(i) || !CHUNK_IS_LOAD(i)) continue;		if (file_offset > chunks[i].offset) continue;		if (cp == NULL || chunks[i].offset < cp->offset) cp = &chunks[i];	}	cur_chunk = cp;}/* * Write the output window window[0..outcnt-1] holding uncompressed * data and update crc. */voidflush_window(void){	static const CHAR8 helicopter[4] = { '|' , '/' , '-' , '\\' };	static UINTN heli_count;	struct segment *cp;	char	*src, *dst;	long	cnt;	if (!outcnt) return;	DBG_PRT((L"%s : flush_window outnct=%d file_offset=%ld\n", LD_NAME, outcnt, file_offset));	Print(L"%c\b",helicopter[heli_count++%4]);	updcrc(window, outcnt);	/* first time, we extract the headers */	if (!bytes_out) {		if (first_block(window, outcnt) < 0) 			error("invalid exec header"); 		nextchunk();	}	bytes_out += outcnt;	src = window;tail:	/* check if user wants to abort */	if (check_abort() == EFI_SUCCESS) goto load_abort;	cp = cur_chunk;	if (cp == NULL || file_offset + outcnt <= cp->offset) {		file_offset += outcnt;		return;	}	/* Does this window begin before the current chunk? */	if (file_offset < cp->offset) {		unsigned long skip = cp->offset - file_offset;		src         += skip;		file_offset += skip;		outcnt      -= skip;	}	dst = (char *)cp->addr + (file_offset - cp->offset);	cnt = cp->offset + cp->size - file_offset;	if (cnt > outcnt) 		cnt = outcnt;	Memcpy(dst, src, cnt);	file_offset += cnt;	outcnt      -= cnt;	src         += cnt;	/* See if we are at the end of this chunk */	if (file_offset == cp->offset + cp->size) {		if (cp->bss_sz) {			dst = (char *)cp->addr + cp->size;			Memset(dst, 0, cp->bss_sz);		}		nextchunk();		/* handle remaining bytes */		if (outcnt) 			goto tail; 	}	return;load_abort:	free_kmem();	error_return = ELILO_LOAD_ABORTED;}static voiderror(char *x){	ERR_PRT((L"%s : %a", LD_NAME, x));	/* will eventually exit with error from gunzip() */}INT32decompress_kernel(VOID){	INT32 ret;	clear_bufs();	makecrc();	Print(L"Uncompressing Linux... ");	ret = gunzip();	if (ret == 0) 		Print(L"done\n");	return ret == 0 ? 0 : -1;}intgunzip_kernel(fops_fd_t fd, kdesc_t *kd){	int ret = -1;	error_return = ELILO_LOAD_ERROR;		window = (void *)alloc(WSIZE, 0);    	if (window == NULL) {        	ERR_PRT((L"%s : allocate output window failed\n", LD_NAME));		return -1;    	}	inbuf = (void *)alloc(INBUFSIZE, 0);    	if (inbuf == NULL) {        	ERR_PRT((L"%s : allocate input window failedr\n", LD_NAME));		goto error;    	}	input_fd   = fd;	insize     = 0;	bytes_out  = 0;	ret = decompress_kernel();error:	if (window) free(window);	if (inbuf) free(inbuf);	if (ret == 0) {		kd->kentry = kernel_entry;		kd->kend   = kernel_end;		kd->kstart = kernel_base;		error_return = ELILO_LOAD_SUCCESS;	}	return error_return;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?