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
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>
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 )
*
* 1 block occurs every 32 K - 1 bytes or when there 50 % compression has been achieved .
* The smallest block type encoding is always used .
*
* 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
* 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
* 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
*/
# define OF(args) args
# define STATIC static
# undef memset
# undef memcpy
# define memzero(s, n) memset ((s), 0, (n))
typedef unsigned char uch ;
typedef unsigned short ush ;
typedef unsigned long ulg ;
2006-12-07 02:14:04 +01:00
# define WSIZE 0x80000000 / * Window size must be at least 32k,
* and a power of two
* We don ' t actually have a window just
* a huge output buffer so I report
* a 2 G windows size , as that should
* always be larger than our output buffer .
*/
2005-04-16 15:20:36 -07:00
2006-12-07 02:14:04 +01:00
static uch * inbuf ; /* input buffer */
static uch * window ; /* Sliding window buffer, (and final output buffer) */
2005-04-16 15:20:36 -07:00
2006-12-07 02:14:04 +01:00
static unsigned insize ; /* valid bytes in inbuf */
static unsigned inptr ; /* index of next byte to be processed in inbuf */
static unsigned outcnt ; /* bytes in output buffer */
2005-04-16 15:20:36 -07:00
/* 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 DEBUG
# define Assert(cond,msg) {if(!(cond)) error(msg);}
# define Trace(x) fprintf x
# define Tracev(x) {if (verbose) fprintf x ;}
# define Tracevv(x) {if (verbose>1) fprintf x ;}
# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
# else
# define Assert(cond,msg)
# define Trace(x)
# define Tracev(x)
# define Tracevv(x)
# define Tracec(c,x)
# define Tracecv(c,x)
# 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 * * ) ;
/*
* 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 ;
static long bytes_out = 0 ;
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 * ) ;
2006-12-07 02:14:04 +01:00
static unsigned long free_mem_ptr ;
static unsigned long free_mem_end_ptr ;
2005-04-16 15:20:36 -07:00
2007-05-02 19:27:15 +02:00
# define HEAP_SIZE 0x4000
2005-04-16 15:20:36 -07:00
static char * vidmem = ( char * ) 0xb8000 ;
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 ;
if ( size < 0 ) error ( " Malloc error " ) ;
if ( free_mem_ptr < = 0 ) error ( " Memory error " ) ;
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 )
{
2006-12-07 02:14:04 +01:00
free_mem_ptr = ( unsigned long ) * ptr ;
2005-04-16 15:20:36 -07:00
}
static void scroll ( void )
{
int i ;
memcpy ( vidmem , vidmem + cols * 2 , ( lines - 1 ) * cols * 2 ) ;
for ( i = ( lines - 1 ) * cols * 2 ; i < lines * cols * 2 ; i + = 2 )
vidmem [ i ] = ' ' ;
}
static void putstr ( const char * s )
{
int x , y , pos ;
char c ;
2007-10-21 16:41:35 -07:00
if ( RM_SCREEN_INFO . orig_video_mode = = 0 & & lines = = 0 & & cols = = 0 )
return ;
2005-04-16 15:20:36 -07:00
x = RM_SCREEN_INFO . orig_x ;
y = RM_SCREEN_INFO . orig_y ;
while ( ( c = * s + + ) ! = ' \0 ' ) {
if ( c = = ' \n ' ) {
x = 0 ;
if ( + + y > = lines ) {
scroll ( ) ;
y - - ;
}
} else {
2008-01-30 13:33:38 +01:00
vidmem [ ( x + cols * y ) * 2 ] = c ;
2005-04-16 15:20:36 -07:00
if ( + + x > = cols ) {
x = 0 ;
if ( + + y > = lines ) {
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
}
2006-06-26 13:57:53 +02: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
for ( i = 0 ; i < n ; i + + ) ss [ i ] = c ;
return s ;
}
2006-06-26 13:57:53 +02: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
2006-06-26 13:57:53 +02:00
for ( i = 0 ; i < n ; i + + ) d [ i ] = s [ i ] ;
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 .
*/
ulg c = crc ; /* temporary variable */
unsigned n ;
uch * in , ch ;
in = window ;
for ( n = 0 ; n < outcnt ; n + + ) {
ch = * in + + ;
c = crc_32_tab [ ( ( int ) c ^ ch ) & 0xff ] ^ ( c > > 8 ) ;
}
crc = c ;
bytes_out + = ( ulg ) outcnt ;
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-01-30 13:33:38 +01:00
asmlinkage void decompress_kernel ( void * rmode , unsigned long heap ,
2008-01-30 13:33:38 +01:00
uch * input_data , unsigned long input_len ,
uch * 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 */
free_mem_end_ptr = heap + 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 ;
2006-12-07 02:14:04 +01:00
if ( ( u32 ) output & ( CONFIG_PHYSICAL_ALIGN - 1 ) )
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 " ) ;
# 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-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
}