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
2008-10-22 22:26:29 -07:00
# define _ASM_X86_DESC_H 1
2008-02-13 20:54:58 +00:00
# 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>
2008-08-02 21:23:36 +02:00
# include <linux/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>
2008-05-29 18:31:14 -04:00
# include <asm/bootparam.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 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
static void error ( char * m ) ;
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
*/
2008-05-29 18:31:14 -04:00
static struct boot_params * real_mode ; /* Pointer to real-mode data */
2008-05-29 18:31:15 -04:00
static int quiet ;
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 ) ;
2009-01-04 22:46:17 +01:00
void * memcpy ( void * dest , const void * src , unsigned n ) ;
2006-06-26 13:57:53 +02:00
2008-06-18 14:04:35 -04:00
static void __putstr ( int , const char * ) ;
# define putstr(__x) __putstr(0, __x)
2005-04-16 15:20:36 -07:00
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 ;
2009-01-04 22:46:17 +01:00
# ifdef CONFIG_KERNEL_GZIP
# include "../../../../lib/decompress_inflate.c"
# endif
# ifdef CONFIG_KERNEL_BZIP2
# include "../../../../lib/decompress_bunzip2.c"
# endif
# ifdef CONFIG_KERNEL_LZMA
# include "../../../../lib/decompress_unlzma.c"
# endif
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 ] = ' ' ;
}
2008-06-18 14:04:35 -04:00
static void __putstr ( int error , const char * s )
2005-04-16 15:20:36 -07:00
{
2008-02-21 00:19:10 +01:00
int x , y , pos ;
2005-04-16 15:20:36 -07:00
char c ;
2008-06-18 14:04:35 -04:00
# ifndef CONFIG_X86_VERBOSE_BOOTUP
if ( ! error )
return ;
# endif
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_32
2008-05-29 18:31:14 -04:00
if ( real_mode - > screen_info . orig_video_mode = = 0 & &
lines = = 0 & & cols = = 0 )
2007-10-21 16:41:35 -07:00
return ;
2008-01-30 13:33:38 +01:00
# endif
2007-10-21 16:41:35 -07:00
2008-05-29 18:31:14 -04:00
x = real_mode - > screen_info . orig_x ;
y = real_mode - > screen_info . orig_y ;
2005-04-16 15:20:36 -07:00
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-08-02 21:23:36 +02: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 - - ;
}
}
}
}
2008-05-29 18:31:14 -04:00
real_mode - > screen_info . orig_x = x ;
real_mode - > screen_info . orig_y = y ;
2005-04-16 15:20:36 -07:00
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-08-02 21:23:36 +02:00
for ( i = 0 ; i < n ; i + + )
ss [ i ] = c ;
2005-04-16 15:20:36 -07:00
return s ;
}
2009-01-04 22:46:17 +01:00
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-08-02 21:23:36 +02: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
}
static void error ( char * x )
{
2008-06-18 14:04:35 -04:00
__putstr ( 1 , " \n \n " ) ;
__putstr ( 1 , x ) ;
__putstr ( 1 , " \n \n -- System halted " ) ;
2005-04-16 15:20:36 -07:00
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 ;
}
2008-05-29 18:31:15 -04:00
if ( ! quiet )
putstr ( " Parsing ELF... " ) ;
2008-02-13 20:54:58 +00:00
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 ;
2008-05-29 18:31:15 -04:00
if ( real_mode - > hdr . loadflags & QUIET_FLAG )
quiet = 1 ;
2008-05-29 18:31:14 -04:00
if ( real_mode - > screen_info . orig_video_mode = = 7 ) {
2005-04-16 15:20:36 -07:00
vidmem = ( char * ) 0xb0000 ;
vidport = 0x3b4 ;
} else {
vidmem = ( char * ) 0xb8000 ;
vidport = 0x3d4 ;
}
2008-05-29 18:31:14 -04:00
lines = real_mode - > screen_info . orig_video_lines ;
cols = real_mode - > screen_info . orig_video_cols ;
2005-04-16 15:20:36 -07:00
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 ;
2006-12-07 02:14:04 +01:00
2009-05-12 11:33:08 -07:00
if ( ( unsigned long ) output & ( MIN_KERNEL_ALIGN - 1 ) )
error ( " Destination address inappropriately aligned " ) ;
2008-01-30 13:33:38 +01:00
# ifdef CONFIG_X86_64
2009-05-12 11:33:08 -07:00
if ( heap > 0x3fffffffffffUL )
2008-01-30 13:33:38 +01:00
error ( " Destination address too large " ) ;
# else
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 " ) ;
2009-05-12 11:33:08 -07:00
# endif
2006-12-07 02:14:04 +01:00
# ifndef CONFIG_RELOCATABLE
2009-05-12 11:33:08 -07:00
if ( ( unsigned long ) 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
2008-05-29 18:31:15 -04:00
if ( ! quiet )
putstr ( " \n Decompressing Linux... " ) ;
2009-01-04 22:46:17 +01:00
decompress ( input_data , input_len , NULL , NULL , output , NULL , error ) ;
2008-02-13 20:54:58 +00:00
parse_elf ( output ) ;
2008-05-29 18:31:15 -04:00
if ( ! quiet )
putstr ( " done. \n Booting the kernel. \n " ) ;
2006-12-07 02:14:04 +01:00
return ;
2005-04-16 15:20:36 -07:00
}