2017-08-20 10:52:22 +02:00
/*
* Definitions and wrapper functions for kernel decompressor
*
* ( C ) 2017 Helge Deller < deller @ gmx . de >
*/
# include <linux/uaccess.h>
2018-10-15 22:14:01 +02:00
# include <linux/elf.h>
2017-08-20 10:52:22 +02:00
# include <asm/unaligned.h>
# include <asm/page.h>
# include "sizes.h"
/*
* gzip declarations
*/
# define STATIC static
# undef memmove
# define memmove memmove
# define memzero(s, n) memset((s), 0, (n))
# define malloc malloc_gzip
# define free free_gzip
/* Symbols defined by linker scripts */
extern char input_data [ ] ;
extern int input_len ;
2017-09-22 21:57:11 +02:00
/* output_len is inserted by the linker possibly at an unaligned address */
extern __le32 output_len __aligned ( 1 ) ;
2017-08-20 10:52:22 +02:00
extern char _text , _end ;
extern char _bss , _ebss ;
extern char _startcode_end ;
extern void startup_continue ( void * entry , unsigned long cmdline ,
unsigned long rd_start , unsigned long rd_end ) __noreturn ;
void error ( char * m ) __noreturn ;
static unsigned long free_mem_ptr ;
static unsigned long free_mem_end_ptr ;
# ifdef CONFIG_KERNEL_GZIP
# include "../../../../lib/decompress_inflate.c"
# endif
# ifdef CONFIG_KERNEL_BZIP2
# include "../../../../lib/decompress_bunzip2.c"
# endif
# ifdef CONFIG_KERNEL_LZ4
# include "../../../../lib/decompress_unlz4.c"
# endif
# ifdef CONFIG_KERNEL_LZMA
# include "../../../../lib/decompress_unlzma.c"
# endif
# ifdef CONFIG_KERNEL_LZO
# include "../../../../lib/decompress_unlzo.c"
# endif
# ifdef CONFIG_KERNEL_XZ
# include "../../../../lib/decompress_unxz.c"
# endif
void * memmove ( void * dest , const void * src , size_t n )
{
const char * s = src ;
char * d = dest ;
if ( d < = s ) {
while ( n - - )
* d + + = * s + + ;
} else {
d + = n ;
s + = n ;
while ( n - - )
* - - d = * - - s ;
}
return dest ;
}
void * memset ( void * s , int c , size_t count )
{
char * xs = ( char * ) s ;
while ( count - - )
* xs + + = c ;
return s ;
}
void * memcpy ( void * d , const void * s , size_t len )
{
char * dest = ( char * ) d ;
const char * source = ( const char * ) s ;
while ( len - - )
* dest + + = * source + + ;
return d ;
}
size_t strlen ( const char * s )
{
const char * sc ;
for ( sc = s ; * sc ! = ' \0 ' ; + + sc )
;
return sc - s ;
}
char * strchr ( const char * s , int c )
{
while ( * s ) {
if ( * s = = ( char ) c )
return ( char * ) s ;
+ + s ;
}
return NULL ;
}
int puts ( const char * s )
{
const char * nuline = s ;
while ( ( nuline = strchr ( s , ' \n ' ) ) ! = NULL ) {
if ( nuline ! = s )
pdc_iodc_print ( s , nuline - s ) ;
2017-12-12 21:32:16 +01:00
pdc_iodc_print ( " \r \n " , 2 ) ;
s = nuline + 1 ;
2017-08-20 10:52:22 +02:00
}
if ( * s ! = ' \0 ' )
pdc_iodc_print ( s , strlen ( s ) ) ;
return 0 ;
}
static int putchar ( int c )
{
char buf [ 2 ] ;
buf [ 0 ] = c ;
buf [ 1 ] = ' \0 ' ;
puts ( buf ) ;
return c ;
}
void __noreturn error ( char * x )
{
puts ( " \n \n " ) ;
puts ( x ) ;
puts ( " \n \n -- System halted " ) ;
while ( 1 ) /* wait forever */
;
}
static int print_hex ( unsigned long num )
{
const char hex [ ] = " 0123456789abcdef " ;
char str [ 40 ] ;
int i = sizeof ( str ) - 1 ;
str [ i - - ] = ' \0 ' ;
do {
str [ i - - ] = hex [ num & 0x0f ] ;
num > > = 4 ;
} while ( num ) ;
str [ i - - ] = ' x ' ;
str [ i ] = ' 0 ' ;
puts ( & str [ i ] ) ;
return 0 ;
}
int printf ( const char * fmt , . . . )
{
va_list args ;
int i = 0 ;
va_start ( args , fmt ) ;
while ( fmt [ i ] ) {
if ( fmt [ i ] ! = ' % ' ) {
put :
putchar ( fmt [ i + + ] ) ;
continue ;
}
if ( fmt [ + + i ] = = ' % ' )
goto put ;
+ + i ;
print_hex ( va_arg ( args , unsigned long ) ) ;
}
va_end ( args ) ;
return 0 ;
}
/* helper functions for libgcc */
void abort ( void )
{
error ( " aborted. " ) ;
}
# undef malloc
void * malloc ( size_t size )
{
return malloc_gzip ( size ) ;
}
# undef free
void free ( void * ptr )
{
return free_gzip ( ptr ) ;
}
static void flush_data_cache ( char * start , unsigned long length )
{
char * end = start + length ;
do {
asm volatile ( " fdc 0(%0) " : : " r " (start)) ;
asm volatile ( " fic 0(%%sr0,%0) " : : " r " (start)) ;
start + = 16 ;
} while ( start < end ) ;
asm volatile ( " fdc 0(%0) " : : " r " (end)) ;
asm ( " sync " ) ;
}
2018-10-15 22:14:01 +02:00
static void parse_elf ( void * output )
{
# ifdef CONFIG_64BIT
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 ) ) ;
if ( ehdr . e_ident [ EI_MAG0 ] ! = ELFMAG0 | |
ehdr . e_ident [ EI_MAG1 ] ! = ELFMAG1 | |
ehdr . e_ident [ EI_MAG2 ] ! = ELFMAG2 | |
ehdr . e_ident [ EI_MAG3 ] ! = ELFMAG3 ) {
error ( " Kernel is not a valid ELF file " ) ;
return ;
}
# ifdef DEBUG
printf ( " Parsing ELF... " ) ;
# endif
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 ) ;
for ( i = 0 ; i < ehdr . e_phnum ; i + + ) {
phdr = & phdrs [ i ] ;
switch ( phdr - > p_type ) {
case PT_LOAD :
dest = ( void * ) ( ( unsigned long ) phdr - > p_paddr &
( __PAGE_OFFSET_DEFAULT - 1 ) ) ;
memmove ( dest , output + phdr - > p_offset , phdr - > p_filesz ) ;
break ;
default :
break ;
}
}
free ( phdrs ) ;
}
2017-08-20 10:52:22 +02:00
unsigned long decompress_kernel ( unsigned int started_wide ,
unsigned int command_line ,
const unsigned int rd_start ,
const unsigned int rd_end )
{
char * output ;
2018-10-15 22:14:01 +02:00
unsigned long vmlinux_addr , vmlinux_len ;
unsigned long kernel_addr , kernel_len ;
2017-08-20 10:52:22 +02:00
# ifdef CONFIG_64BIT
parisc_narrow_firmware = 0 ;
# endif
set_firmware_width_unlocked ( ) ;
2018-10-15 22:14:01 +02:00
putchar ( ' D ' ) ; /* if you get this D and no more, string storage */
2017-08-20 10:52:22 +02:00
/* in $GLOBAL$ is wrong or %dp is wrong */
2018-10-15 22:14:01 +02:00
puts ( " ecompressing Linux... " ) ;
2017-08-20 10:52:22 +02:00
2018-10-15 22:14:01 +02:00
/* where the final bits are stored */
kernel_addr = KERNEL_BINARY_TEXT_START ;
kernel_len = __pa ( SZ_end ) - __pa ( SZparisc_kernel_start ) ;
if ( ( unsigned long ) & _startcode_end > kernel_addr )
2017-08-20 10:52:22 +02:00
error ( " Bootcode overlaps kernel code " ) ;
2018-10-15 22:14:01 +02:00
/*
* Calculate addr to where the vmlinux ELF file shall be decompressed .
* Assembly code in head . S positioned the stack directly behind bss , so
* leave 2 MB for the stack .
*/
vmlinux_addr = ( unsigned long ) & _ebss + 2 * 1024 * 1024 ;
vmlinux_len = get_unaligned_le32 ( & output_len ) ;
output = ( char * ) vmlinux_addr ;
2017-08-20 10:52:22 +02:00
/*
* Initialize free_mem_ptr and free_mem_end_ptr .
*/
2018-10-15 22:14:01 +02:00
free_mem_ptr = vmlinux_addr + vmlinux_len ;
2017-08-20 10:52:22 +02:00
/* Limit memory for bootoader to 1GB */
# define ARTIFICIAL_LIMIT (1*1024*1024*1024)
free_mem_end_ptr = PAGE0 - > imm_max_mem ;
if ( free_mem_end_ptr > ARTIFICIAL_LIMIT )
free_mem_end_ptr = ARTIFICIAL_LIMIT ;
# ifdef CONFIG_BLK_DEV_INITRD
/* if we have ramdisk this is at end of memory */
if ( rd_start & & rd_start < free_mem_end_ptr )
free_mem_end_ptr = rd_start ;
# endif
2018-10-15 22:14:01 +02:00
if ( free_mem_ptr > = free_mem_end_ptr )
error ( " Kernel too big for machine. " ) ;
2017-08-20 10:52:22 +02:00
# ifdef DEBUG
2018-10-15 22:14:01 +02:00
printf ( " \n " ) ;
2017-08-20 10:52:22 +02:00
printf ( " startcode_end = %x \n " , & _startcode_end ) ;
printf ( " commandline = %x \n " , command_line ) ;
printf ( " rd_start = %x \n " , rd_start ) ;
printf ( " rd_end = %x \n " , rd_end ) ;
printf ( " free_ptr = %x \n " , free_mem_ptr ) ;
printf ( " free_ptr_end = %x \n " , free_mem_end_ptr ) ;
printf ( " input_data = %x \n " , input_data ) ;
printf ( " input_len = %x \n " , input_len ) ;
printf ( " output = %x \n " , output ) ;
2018-10-15 22:14:01 +02:00
printf ( " output_len = %x \n " , vmlinux_len ) ;
printf ( " kernel_addr = %x \n " , kernel_addr ) ;
printf ( " kernel_len = %x \n " , kernel_len ) ;
2017-08-20 10:52:22 +02:00
# endif
__decompress ( input_data , input_len , NULL , NULL ,
output , 0 , NULL , error ) ;
2018-10-15 22:14:01 +02:00
parse_elf ( output ) ;
2017-08-20 10:52:22 +02:00
2018-10-15 22:14:01 +02:00
output = ( char * ) kernel_addr ;
flush_data_cache ( output , kernel_len ) ;
2017-08-20 10:52:22 +02:00
2018-10-15 22:14:01 +02:00
printf ( " done. \n Booting the kernel. \n " ) ;
2017-08-20 10:52:22 +02:00
return ( unsigned long ) output ;
}