2005-04-16 15:20:36 -07:00
/*
* misc . c
2008-01-30 13:33:38 +01:00
*
* This is a collection of several routines from gzip - 1.0 .3
2005-04-16 15:20:36 -07:00
* adapted for Linux .
*
* malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
* puts by Nick Holloway 1993 , better puts by Martin Mares 1995
* High loaded stuff by Hans Lermen & Werner Almesberger , Feb . 1996
*/
2008-01-30 13:33:38 +01:00
/*
* we have to be careful , because no indirections are allowed here , and
* paravirt_ops is a kind of one . As it will only run in baremetal anyway ,
* we just keep it from happening
*/
2006-12-07 02:14:07 +01:00
# undef CONFIG_PARAVIRT
2008-02-13 20:54:58 +00:00
# ifdef CONFIG_X86_32
# define _ASM_DESC_H_ 1
# endif
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_64
# define _LINUX_STRING_H_ 1
# define __LINUX_BITMAP_H 1
# endif
2005-04-16 15:20:36 -07:00
# include <linux/linkage.h>
2006-01-08 01:04:54 -08:00
# include <linux/screen_info.h>
2008-02-13 20:54:58 +00:00
# include <linux/elf.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
2006-12-07 02:14:04 +01:00
# include <asm/page.h>
2006-12-07 02:14:04 +01:00
# include <asm/boot.h>
2006-12-07 02:14:04 +01:00
/* WARNING!!
* This code is compiled with - fPIC and it is relocated dynamically
* at run time , but no relocation processing is performed .
* This means that it is not safe to place pointers in static structures .
*/
/*
* Getting to provable safe in place decompression is hard .
2007-10-20 01:13:56 +02:00
* Worst case behaviours need to be analyzed .
2006-12-07 02:14:04 +01:00
* Background information :
*
* The file layout is :
* magic [ 2 ]
* method [ 1 ]
* flags [ 1 ]
* timestamp [ 4 ]
* extraflags [ 1 ]
* os [ 1 ]
* compressed data blocks [ N ]
* crc [ 4 ] orig_len [ 4 ]
*
* resulting in 18 bytes of non compressed data overhead .
*
* Files divided into blocks
* 1 bit ( last block flag )
* 2 bits ( block type )
*
2008-02-21 05:03:48 +01:00
* 1 block occurs every 32 K - 1 bytes or when there 50 % compression
* has been achieved . The smallest block type encoding is always used .
2006-12-07 02:14:04 +01:00
*
* stored :
* 32 bits length in bytes .
*
* fixed :
* magic fixed tree .
* symbols .
*
* dynamic :
* dynamic tree encoding .
* symbols .
*
*
* The buffer for decompression in place is the length of the
* uncompressed data , plus a small amount extra to keep the algorithm safe .
* The compressed data is placed at the end of the buffer . The output
* pointer is placed at the start of the buffer and the input pointer
* is placed where the compressed data starts . Problems will occur
* when the output pointer overruns the input pointer .
*
* The output pointer can only overrun the input pointer if the input
* pointer is moving faster than the output pointer . A condition only
* triggered by data whose compressed form is larger than the uncompressed
* form .
*
* The worst case at the block level is a growth of the compressed data
* of 5 bytes per 32767 bytes .
*
* The worst case internal to a compressed block is very hard to figure .
* The worst case can at least be boundined by having one bit that represents
* 32764 bytes and then all of the rest of the bytes representing the very
* very last byte .
*
* All of which is enough to compute an amount of extra data that is required
* to be safe . To avoid problems at the block level allocating 5 extra bytes
2008-02-21 05:03:48 +01:00
* per 32767 bytes of data is sufficient . To avoind problems internal to a
* block adding an extra 32767 bytes ( the worst case uncompressed block size )
* is sufficient , to ensure that in the worst case the decompressed data for
2006-12-07 02:14:04 +01:00
* block will stop the byte before the compressed data for a block begins .
* To avoid problems with the compressed data ' s meta information an extra 18
* bytes are needed . Leading to the formula :
*
* extra_bytes = ( uncompressed_size > > 12 ) + 32768 + 18 + decompressor_size .
*
* Adding 8 bytes per 32 K is a bit excessive but much easier to calculate .
* Adding 32768 instead of 32767 just makes for round numbers .
* Adding the decompressor_size is necessary as it musht live after all
* of the data as well . Last I measured the decompressor is about 14 K .
2007-10-20 01:13:56 +02:00
* 10 K of actual data and 4 K of bss .
2006-12-07 02:14:04 +01:00
*
*/
2005-04-16 15:20:36 -07:00
/*
* gzip declarations
*/
2008-02-21 05:03:48 +01:00
# define OF(args) args
# define STATIC static
2005-04-16 15:20:36 -07:00
# undef memset
# undef memcpy
2008-02-21 05:03:48 +01:00
# define memzero(s, n) memset((s), 0, (n))
2005-04-16 15:20:36 -07:00
2008-02-21 05:03:48 +01:00
typedef unsigned char uch ;
typedef unsigned short ush ;
typedef unsigned long ulg ;
2005-04-16 15:20:36 -07:00
2008-02-21 05:03:48 +01:00
/*
* Window size must be at least 32 k , and a power of two .
* We don ' t actually have a window just a huge output buffer ,
* so we report a 2 G window size , as that should always be
* larger than our output buffer :
*/
# define WSIZE 0x80000000
/* Input buffer: */
static unsigned char * inbuf ;
/* Sliding window buffer (and final output buffer): */
static unsigned char * window ;
/* Valid bytes in inbuf: */
static unsigned insize ;
2005-04-16 15:20:36 -07:00
2008-02-21 05:03:48 +01:00
/* Index of next byte to be processed in inbuf: */
static unsigned inptr ;
2005-04-16 15:20:36 -07:00
2008-02-21 05:03:48 +01:00
/* Bytes in output buffer: */
static unsigned outcnt ;
2005-04-16 15:20:36 -07:00
/* gzip flag byte */
2008-02-21 05:03:48 +01:00
# define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
# define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gz file */
# define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
# define ORIG_NAM 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 */
2005-04-16 15:20:36 -07:00
2008-02-21 05:03:48 +01:00
# define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
2008-02-21 00:19:10 +01:00
2005-04-16 15:20:36 -07:00
/* Diagnostic functions */
# ifdef DEBUG
2008-02-21 05:03:48 +01:00
# define Assert(cond, msg) do { if (!(cond)) error(msg); } while (0)
# define Trace(x) do { fprintf x; } while (0)
# define Tracev(x) do { if (verbose) fprintf x ; } while (0)
# define Tracevv(x) do { if (verbose > 1) fprintf x ; } while (0)
# define Tracec(c, x) do { if (verbose && (c)) fprintf x ; } while (0)
# define Tracecv(c, x) do { if (verbose > 1 && (c)) fprintf x ; } while (0)
2005-04-16 15:20:36 -07:00
# else
2008-02-21 00:19:10 +01:00
# define Assert(cond, msg)
2005-04-16 15:20:36 -07:00
# define Trace(x)
# define Tracev(x)
# define Tracevv(x)
2008-02-21 00:19:10 +01:00
# define Tracec(c, x)
# define Tracecv(c, x)
2005-04-16 15:20:36 -07:00
# endif
static int fill_inbuf ( void ) ;
static void flush_window ( void ) ;
static void error ( char * m ) ;
static void gzip_mark ( void * * ) ;
static void gzip_release ( void * * ) ;
2008-02-21 00:19:10 +01:00
2005-04-16 15:20:36 -07:00
/*
* This is set up by the setup - routine at boot - time
*/
static unsigned char * real_mode ; /* Pointer to real-mode data */
# define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
# ifndef STANDARD_MEMORY_BIOS_CALL
# define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
# endif
# define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
2006-06-26 13:57:53 +02:00
extern unsigned char input_data [ ] ;
2005-04-16 15:20:36 -07:00
extern int input_len ;
2008-02-21 00:19:10 +01:00
static long bytes_out ;
2005-04-16 15:20:36 -07:00
static void * malloc ( int size ) ;
static void free ( void * where ) ;
2006-06-26 13:57:53 +02:00
static void * memset ( void * s , int c , unsigned n ) ;
static void * memcpy ( void * dest , const void * src , unsigned n ) ;
2005-04-16 15:20:36 -07:00
static void putstr ( const char * ) ;
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_64
# define memptr long
# else
# define memptr unsigned
# endif
static memptr free_mem_ptr ;
static memptr free_mem_end_ptr ;
2005-04-16 15:20:36 -07:00
2008-04-06 14:47:00 +02:00
static char * vidmem ;
2005-04-16 15:20:36 -07:00
static int vidport ;
static int lines , cols ;
# ifdef CONFIG_X86_NUMAQ
2006-12-07 02:14:13 +01:00
void * xquad_portio ;
2005-04-16 15:20:36 -07:00
# endif
# include "../../../../lib/inflate.c"
static void * malloc ( int size )
{
void * p ;
2008-02-21 00:19:10 +01:00
if ( size < 0 )
error ( " Malloc error " ) ;
if ( free_mem_ptr < = 0 )
error ( " Memory error " ) ;
2005-04-16 15:20:36 -07:00
free_mem_ptr = ( free_mem_ptr + 3 ) & ~ 3 ; /* Align */
p = ( void * ) free_mem_ptr ;
free_mem_ptr + = size ;
if ( free_mem_ptr > = free_mem_end_ptr )
error ( " Out of memory " ) ;
return p ;
}
static void free ( void * where )
{ /* Don't care */
}
static void gzip_mark ( void * * ptr )
{
* ptr = ( void * ) free_mem_ptr ;
}
static void gzip_release ( void * * ptr )
{
2008-01-30 13:33:38 +01:00
free_mem_ptr = ( memptr ) * ptr ;
2005-04-16 15:20:36 -07:00
}
2008-02-21 00:19:10 +01:00
2005-04-16 15:20:36 -07:00
static void scroll ( void )
{
int i ;
2008-02-21 00:19:10 +01:00
memcpy ( vidmem , vidmem + cols * 2 , ( lines - 1 ) * cols * 2 ) ;
for ( i = ( lines - 1 ) * cols * 2 ; i < lines * cols * 2 ; i + = 2 )
2005-04-16 15:20:36 -07:00
vidmem [ i ] = ' ' ;
}
static void putstr ( const char * s )
{
2008-02-21 00:19:10 +01:00
int x , y , pos ;
2005-04-16 15:20:36 -07:00
char c ;
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_32
2007-10-21 16:41:35 -07:00
if ( RM_SCREEN_INFO . orig_video_mode = = 0 & & lines = = 0 & & cols = = 0 )
return ;
2008-01-30 13:33:38 +01:00
# endif
2007-10-21 16:41:35 -07:00
2005-04-16 15:20:36 -07:00
x = RM_SCREEN_INFO . orig_x ;
y = RM_SCREEN_INFO . orig_y ;
2008-02-21 00:19:10 +01:00
while ( ( c = * s + + ) ! = ' \0 ' ) {
if ( c = = ' \n ' ) {
2005-04-16 15:20:36 -07:00
x = 0 ;
2008-02-21 00:19:10 +01:00
if ( + + y > = lines ) {
2005-04-16 15:20:36 -07:00
scroll ( ) ;
y - - ;
}
} else {
2008-01-30 13:33:38 +01:00
vidmem [ ( x + cols * y ) * 2 ] = c ;
2008-02-21 00:19:10 +01:00
if ( + + x > = cols ) {
2005-04-16 15:20:36 -07:00
x = 0 ;
2008-02-21 00:19:10 +01:00
if ( + + y > = lines ) {
2005-04-16 15:20:36 -07:00
scroll ( ) ;
y - - ;
}
}
}
}
RM_SCREEN_INFO . orig_x = x ;
RM_SCREEN_INFO . orig_y = y ;
pos = ( x + cols * y ) * 2 ; /* Update cursor position */
2008-01-30 13:30:05 +01:00
outb ( 14 , vidport ) ;
outb ( 0xff & ( pos > > 9 ) , vidport + 1 ) ;
outb ( 15 , vidport ) ;
outb ( 0xff & ( pos > > 1 ) , vidport + 1 ) ;
2005-04-16 15:20:36 -07:00
}
2008-02-21 00:19:10 +01:00
static void * memset ( void * s , int c , unsigned n )
2005-04-16 15:20:36 -07:00
{
int i ;
2008-01-30 13:33:23 +01:00
char * ss = s ;
2005-04-16 15:20:36 -07:00
2008-02-21 00:19:10 +01:00
for ( i = 0 ; i < n ; i + + ) ss [ i ] = c ;
2005-04-16 15:20:36 -07:00
return s ;
}
2008-02-21 00:19:10 +01:00
static void * memcpy ( void * dest , const void * src , unsigned n )
2005-04-16 15:20:36 -07:00
{
int i ;
2008-01-30 13:33:23 +01:00
const char * s = src ;
char * d = dest ;
2005-04-16 15:20:36 -07:00
2008-02-21 00:19:10 +01:00
for ( i = 0 ; i < n ; i + + ) d [ i ] = s [ i ] ;
2006-06-26 13:57:53 +02:00
return dest ;
2005-04-16 15:20:36 -07:00
}
/* ===========================================================================
* Fill the input buffer . This is called only when the buffer is empty
* and at least one byte is really needed .
*/
static int fill_inbuf ( void )
{
2006-12-07 02:14:04 +01:00
error ( " ran out of input data " ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
/* ===========================================================================
* Write the output window window [ 0. . outcnt - 1 ] and update crc and bytes_out .
* ( Used for the decompressed data only . )
*/
static void flush_window ( void )
{
2006-12-07 02:14:04 +01:00
/* With my window equal to my output buffer
* I only need to compute the crc here .
*/
2008-02-21 05:03:48 +01:00
unsigned long c = crc ; /* temporary variable */
2006-12-07 02:14:04 +01:00
unsigned n ;
2008-02-21 05:03:48 +01:00
unsigned char * in , ch ;
2006-12-07 02:14:04 +01:00
in = window ;
for ( n = 0 ; n < outcnt ; n + + ) {
ch = * in + + ;
c = crc_32_tab [ ( ( int ) c ^ ch ) & 0xff ] ^ ( c > > 8 ) ;
}
crc = c ;
2008-02-21 05:03:48 +01:00
bytes_out + = ( unsigned long ) outcnt ;
2006-12-07 02:14:04 +01:00
outcnt = 0 ;
2005-04-16 15:20:36 -07:00
}
static void error ( char * x )
{
putstr ( " \n \n " ) ;
putstr ( x ) ;
putstr ( " \n \n -- System halted " ) ;
2008-01-30 13:32:31 +01:00
while ( 1 )
asm ( " hlt " ) ;
2005-04-16 15:20:36 -07:00
}
2008-02-13 20:54:58 +00:00
static void parse_elf ( void * output )
{
# ifdef CONFIG_X86_64
Elf64_Ehdr ehdr ;
Elf64_Phdr * phdrs , * phdr ;
# else
Elf32_Ehdr ehdr ;
Elf32_Phdr * phdrs , * phdr ;
# endif
void * dest ;
int i ;
memcpy ( & ehdr , output , sizeof ( ehdr ) ) ;
2008-02-21 00:19:10 +01:00
if ( ehdr . e_ident [ EI_MAG0 ] ! = ELFMAG0 | |
2008-02-13 20:54:58 +00:00
ehdr . e_ident [ EI_MAG1 ] ! = ELFMAG1 | |
ehdr . e_ident [ EI_MAG2 ] ! = ELFMAG2 | |
2008-02-21 00:19:10 +01:00
ehdr . e_ident [ EI_MAG3 ] ! = ELFMAG3 ) {
2008-02-13 20:54:58 +00:00
error ( " Kernel is not a valid ELF file " ) ;
return ;
}
putstr ( " Parsing ELF... " ) ;
phdrs = malloc ( sizeof ( * phdrs ) * ehdr . e_phnum ) ;
if ( ! phdrs )
error ( " Failed to allocate space for phdrs " ) ;
memcpy ( phdrs , output + ehdr . e_phoff , sizeof ( * phdrs ) * ehdr . e_phnum ) ;
2008-02-21 00:19:10 +01:00
for ( i = 0 ; i < ehdr . e_phnum ; i + + ) {
2008-02-13 20:54:58 +00:00
phdr = & phdrs [ i ] ;
switch ( phdr - > p_type ) {
case PT_LOAD :
# ifdef CONFIG_RELOCATABLE
dest = output ;
dest + = ( phdr - > p_paddr - LOAD_PHYSICAL_ADDR ) ;
# else
2008-02-21 00:19:10 +01:00
dest = ( void * ) ( phdr - > p_paddr ) ;
2008-02-13 20:54:58 +00:00
# endif
memcpy ( dest ,
output + phdr - > p_offset ,
phdr - > p_filesz ) ;
break ;
default : /* Ignore other PT_* */ break ;
}
}
}
2008-01-30 13:33:38 +01:00
asmlinkage void decompress_kernel ( void * rmode , memptr heap ,
2008-02-21 05:03:48 +01:00
unsigned char * input_data ,
unsigned long input_len ,
unsigned char * output )
2005-04-16 15:20:36 -07:00
{
real_mode = rmode ;
if ( RM_SCREEN_INFO . orig_video_mode = = 7 ) {
vidmem = ( char * ) 0xb0000 ;
vidport = 0x3b4 ;
} else {
vidmem = ( char * ) 0xb8000 ;
vidport = 0x3d4 ;
}
lines = RM_SCREEN_INFO . orig_video_lines ;
cols = RM_SCREEN_INFO . orig_video_cols ;
2008-01-30 13:33:38 +01:00
window = output ; /* Output buffer (Normally at 1M) */
2008-01-30 13:33:38 +01:00
free_mem_ptr = heap ; /* Heap */
2008-04-08 12:54:30 +02:00
free_mem_end_ptr = heap + BOOT_HEAP_SIZE ;
2008-01-30 13:33:38 +01:00
inbuf = input_data ; /* Input buffer */
2006-12-07 02:14:04 +01:00
insize = input_len ;
inptr = 0 ;
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_64
2008-02-21 05:03:48 +01:00
if ( ( unsigned long ) output & ( __KERNEL_ALIGN - 1 ) )
2008-01-30 13:33:38 +01:00
error ( " Destination address not 2M aligned " ) ;
2008-02-21 05:03:48 +01:00
if ( ( unsigned long ) output > = 0xffffffffffUL )
2008-01-30 13:33:38 +01:00
error ( " Destination address too large " ) ;
# else
2008-02-21 05:03:48 +01:00
if ( ( u32 ) output & ( CONFIG_PHYSICAL_ALIGN - 1 ) )
2006-12-07 02:14:04 +01:00
error ( " Destination address not CONFIG_PHYSICAL_ALIGN aligned " ) ;
2008-01-30 13:33:38 +01:00
if ( heap > ( ( - __PAGE_OFFSET - ( 512 < < 20 ) - 1 ) & 0x7fffffff ) )
2006-12-07 02:14:04 +01:00
error ( " Destination address too large " ) ;
# ifndef CONFIG_RELOCATABLE
2006-12-07 02:14:04 +01:00
if ( ( u32 ) output ! = LOAD_PHYSICAL_ADDR )
2006-12-07 02:14:04 +01:00
error ( " Wrong destination address " ) ;
2008-01-30 13:33:38 +01:00
# endif
2006-12-07 02:14:04 +01:00
# endif
2005-04-16 15:20:36 -07:00
makecrc ( ) ;
2008-01-30 13:33:38 +01:00
putstr ( " \n Decompressing Linux... " ) ;
2005-04-16 15:20:36 -07:00
gunzip ( ) ;
2008-02-13 20:54:58 +00:00
parse_elf ( output ) ;
2008-01-30 13:33:38 +01:00
putstr ( " done. \n Booting the kernel. \n " ) ;
2006-12-07 02:14:04 +01:00
return ;
2005-04-16 15:20:36 -07:00
}