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
*/
2010-08-02 16:21:22 -07:00
# include "misc.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
*/
2010-08-02 16:21:22 -07:00
struct boot_params * real_mode ; /* Pointer to real-mode data */
2008-05-29 18:31:15 -04:00
static int quiet ;
2010-08-02 16:21:22 -07:00
static int debug ;
2005-04-16 15:20:36 -07:00
2010-02-05 09:37:07 -05:00
void * memset ( void * s , int c , size_t n ) ;
void * memcpy ( void * dest , const void * src , size_t n ) ;
2006-06-26 13:57:53 +02: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
2011-01-12 17:01:24 -08:00
# ifdef CONFIG_KERNEL_XZ
# include "../../../../lib/decompress_unxz.c"
# endif
2010-01-08 14:42:45 -08:00
# ifdef CONFIG_KERNEL_LZO
# include "../../../../lib/decompress_unlzo.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 ] = ' ' ;
}
2010-08-02 16:21:22 -07:00
# define XMTRDY 0x20
# define TXR 0 /* Transmit register (WRITE) */
# define LSR 5 /* Line Status */
static void serial_putchar ( int ch )
{
unsigned timeout = 0xffff ;
while ( ( inb ( early_serial_base + LSR ) & XMTRDY ) = = 0 & & - - timeout )
cpu_relax ( ) ;
outb ( ch , early_serial_base + TXR ) ;
}
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
2010-08-02 16:21:22 -07:00
if ( early_serial_base ) {
const char * str = s ;
while ( * str ) {
if ( * str = = ' \n ' )
serial_putchar ( ' \r ' ) ;
serial_putchar ( * str + + ) ;
}
}
2008-06-18 14:04:35 -04:00
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-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
}
2010-02-05 09:37:07 -05:00
void * memset ( void * s , int c , size_t 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 ;
}
2010-10-08 09:47:33 +08:00
# ifdef CONFIG_X86_32
2010-02-05 09:37:07 -05:00
void * memcpy ( void * dest , const void * src , size_t n )
2005-04-16 15:20:36 -07:00
{
2010-10-08 09:47:33 +08:00
int d0 , d1 , d2 ;
asm volatile (
" rep ; movsl \n \t "
" movl %4,%%ecx \n \t "
" rep ; movsb \n \t "
: " =&c " ( d0 ) , " =&D " ( d1 ) , " =&S " ( d2 )
: " 0 " ( n > > 2 ) , " g " ( n & 3 ) , " 1 " ( dest ) , " 2 " ( src )
: " memory " ) ;
2005-04-16 15:20:36 -07:00
2006-06-26 13:57:53 +02:00
return dest ;
2005-04-16 15:20:36 -07:00
}
2010-10-08 09:47:33 +08:00
# else
void * memcpy ( void * dest , const void * src , size_t n )
{
long d0 , d1 , d2 ;
asm volatile (
" rep ; movsq \n \t "
" movq %4,%%rcx \n \t "
" rep ; movsb \n \t "
: " =&c " ( d0 ) , " =&D " ( d1 ) , " =&S " ( d2 )
: " 0 " ( n > > 3 ) , " g " ( n & 7 ) , " 1 " ( dest ) , " 2 " ( src )
: " memory " ) ;
2005-04-16 15:20:36 -07:00
2010-10-08 09:47:33 +08:00
return dest ;
}
# endif
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 ;
}
}
2012-01-23 23:34:59 +01:00
free ( phdrs ) ;
2008-02-13 20:54:58 +00:00
}
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 ;
2010-08-02 16:21:22 -07:00
if ( cmdline_find_option_bool ( " quiet " ) )
2008-05-29 18:31:15 -04:00
quiet = 1 ;
2010-08-02 16:21:22 -07:00
if ( cmdline_find_option_bool ( " debug " ) )
debug = 1 ;
2008-05-29 18:31:15 -04:00
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
2010-08-02 16:21:22 -07:00
console_init ( ) ;
if ( debug )
putstr ( " early console in decompress_kernel \n " ) ;
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
2010-12-16 19:11:09 -08:00
if ( heap > ( ( - __PAGE_OFFSET - ( 128 < < 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
}