2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) Paul Mackerras 1997.
*
* Updates for PPC64 by Todd Inglett , Dave Engebretsen & Peter Bergner .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
2005-08-08 13:24:38 +10:00
# include <stdarg.h>
# include <stddef.h>
# include "elf.h"
# include "page.h"
# include "string.h"
# include "stdio.h"
2005-04-16 15:20:36 -07:00
# include "zlib.h"
2006-09-19 14:05:08 +10:00
# include "ops.h"
# include "flatdevtree.h"
2005-08-08 13:24:38 +10:00
extern void flush_cache ( void * , unsigned long ) ;
2005-04-16 15:20:36 -07:00
extern char _start [ ] ;
2005-10-28 17:46:45 -07:00
extern char __bss_start [ ] ;
2005-09-06 15:50:02 -07:00
extern char _end [ ] ;
2005-04-16 15:20:36 -07:00
extern char _vmlinux_start [ ] ;
extern char _vmlinux_end [ ] ;
extern char _initrd_start [ ] ;
extern char _initrd_end [ ] ;
2006-10-16 13:49:27 -07:00
extern char _dtb_start [ ] ;
extern char _dtb_end [ ] ;
2005-04-16 15:20:36 -07:00
struct addr_range {
unsigned long addr ;
unsigned long size ;
unsigned long memsize ;
} ;
2005-10-28 17:46:47 -07:00
static struct addr_range vmlinux ;
static struct addr_range vmlinuz ;
static struct addr_range initrd ;
2005-04-16 15:20:36 -07:00
2005-11-16 13:38:21 +11:00
static unsigned long elfoffset ;
2006-09-19 14:05:08 +10:00
static int is_64bit ;
2005-11-16 13:38:21 +11:00
2006-09-19 14:05:08 +10:00
/* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */
static char scratch [ 46912 ] ;
2005-10-28 17:46:40 -07:00
static char elfheader [ 256 ] ;
2005-10-28 17:46:38 -07:00
2006-09-19 14:05:08 +10:00
typedef void ( * kernel_entry_t ) ( unsigned long , unsigned long , void * ) ;
2005-04-16 15:20:36 -07:00
# undef DEBUG
2005-10-28 17:46:41 -07:00
# define HEAD_CRC 2
# define EXTRA_FIELD 4
# define ORIG_NAME 8
# define COMMENT 0x10
# define RESERVED 0xe0
static void gunzip ( void * dst , int dstlen , unsigned char * src , int * lenp )
{
z_stream s ;
int r , i , flags ;
/* skip header */
i = 10 ;
flags = src [ 3 ] ;
if ( src [ 2 ] ! = Z_DEFLATED | | ( flags & RESERVED ) ! = 0 ) {
printf ( " bad gzipped data \n \r " ) ;
exit ( ) ;
}
if ( ( flags & EXTRA_FIELD ) ! = 0 )
i = 12 + src [ 10 ] + ( src [ 11 ] < < 8 ) ;
if ( ( flags & ORIG_NAME ) ! = 0 )
while ( src [ i + + ] ! = 0 )
;
if ( ( flags & COMMENT ) ! = 0 )
while ( src [ i + + ] ! = 0 )
;
if ( ( flags & HEAD_CRC ) ! = 0 )
i + = 2 ;
if ( i > = * lenp ) {
printf ( " gunzip: ran out of data in header \n \r " ) ;
exit ( ) ;
}
if ( zlib_inflate_workspacesize ( ) > sizeof ( scratch ) ) {
printf ( " gunzip needs more mem \n " ) ;
exit ( ) ;
}
memset ( & s , 0 , sizeof ( s ) ) ;
s . workspace = scratch ;
r = zlib_inflateInit2 ( & s , - MAX_WBITS ) ;
if ( r ! = Z_OK ) {
printf ( " inflateInit2 returned %d \n \r " , r ) ;
exit ( ) ;
}
s . next_in = src + i ;
s . avail_in = * lenp - i ;
s . next_out = dst ;
s . avail_out = dstlen ;
r = zlib_inflate ( & s , Z_FULL_FLUSH ) ;
if ( r ! = Z_OK & & r ! = Z_STREAM_END ) {
printf ( " inflate returned %d msg: %s \n \r " , r , s . msg ) ;
exit ( ) ;
}
* lenp = s . next_out - ( unsigned char * ) dst ;
zlib_inflateEnd ( & s ) ;
}
2005-11-16 13:38:21 +11:00
static int is_elf64 ( void * hdr )
{
Elf64_Ehdr * elf64 = hdr ;
Elf64_Phdr * elf64ph ;
unsigned int i ;
if ( ! ( elf64 - > e_ident [ EI_MAG0 ] = = ELFMAG0 & &
elf64 - > e_ident [ EI_MAG1 ] = = ELFMAG1 & &
elf64 - > e_ident [ EI_MAG2 ] = = ELFMAG2 & &
elf64 - > e_ident [ EI_MAG3 ] = = ELFMAG3 & &
elf64 - > e_ident [ EI_CLASS ] = = ELFCLASS64 & &
elf64 - > e_ident [ EI_DATA ] = = ELFDATA2MSB & &
elf64 - > e_type = = ET_EXEC & &
elf64 - > e_machine = = EM_PPC64 ) )
return 0 ;
elf64ph = ( Elf64_Phdr * ) ( ( unsigned long ) elf64 +
( unsigned long ) elf64 - > e_phoff ) ;
for ( i = 0 ; i < ( unsigned int ) elf64 - > e_phnum ; i + + , elf64ph + + )
2006-01-30 14:28:03 +01:00
if ( elf64ph - > p_type = = PT_LOAD )
2005-11-16 13:38:21 +11:00
break ;
if ( i > = ( unsigned int ) elf64 - > e_phnum )
return 0 ;
elfoffset = ( unsigned long ) elf64ph - > p_offset ;
vmlinux . size = ( unsigned long ) elf64ph - > p_filesz + elfoffset ;
vmlinux . memsize = ( unsigned long ) elf64ph - > p_memsz + elfoffset ;
2006-01-14 15:04:06 +11:00
2006-09-19 14:05:08 +10:00
is_64bit = 1 ;
2005-11-16 13:38:21 +11:00
return 1 ;
}
static int is_elf32 ( void * hdr )
{
Elf32_Ehdr * elf32 = hdr ;
Elf32_Phdr * elf32ph ;
unsigned int i ;
if ( ! ( elf32 - > e_ident [ EI_MAG0 ] = = ELFMAG0 & &
elf32 - > e_ident [ EI_MAG1 ] = = ELFMAG1 & &
elf32 - > e_ident [ EI_MAG2 ] = = ELFMAG2 & &
elf32 - > e_ident [ EI_MAG3 ] = = ELFMAG3 & &
elf32 - > e_ident [ EI_CLASS ] = = ELFCLASS32 & &
elf32 - > e_ident [ EI_DATA ] = = ELFDATA2MSB & &
elf32 - > e_type = = ET_EXEC & &
elf32 - > e_machine = = EM_PPC ) )
return 0 ;
elf32 = ( Elf32_Ehdr * ) elfheader ;
elf32ph = ( Elf32_Phdr * ) ( ( unsigned long ) elf32 + elf32 - > e_phoff ) ;
for ( i = 0 ; i < elf32 - > e_phnum ; i + + , elf32ph + + )
2006-01-30 14:28:03 +01:00
if ( elf32ph - > p_type = = PT_LOAD )
2005-11-16 13:38:21 +11:00
break ;
if ( i > = elf32 - > e_phnum )
return 0 ;
elfoffset = elf32ph - > p_offset ;
vmlinux . size = elf32ph - > p_filesz + elf32ph - > p_offset ;
vmlinux . memsize = elf32ph - > p_memsz + elf32ph - > p_offset ;
return 1 ;
}
2006-11-16 15:31:32 +11:00
static void prep_kernel ( unsigned long a1 , unsigned long a2 )
2005-04-16 15:20:36 -07:00
{
2005-10-28 17:46:40 -07:00
int len ;
2006-01-14 15:04:06 +11:00
2005-11-16 13:38:21 +11:00
vmlinuz . addr = ( unsigned long ) _vmlinux_start ;
vmlinuz . size = ( unsigned long ) ( _vmlinux_end - _vmlinux_start ) ;
/* gunzip the ELF header of the kernel */
if ( * ( unsigned short * ) vmlinuz . addr = = 0x1f8b ) {
len = vmlinuz . size ;
gunzip ( elfheader , sizeof ( elfheader ) ,
( unsigned char * ) vmlinuz . addr , & len ) ;
} else
2006-09-19 14:05:08 +10:00
memcpy ( elfheader , ( const void * ) vmlinuz . addr ,
sizeof ( elfheader ) ) ;
2005-11-16 13:38:21 +11:00
if ( ! is_elf64 ( elfheader ) & & ! is_elf32 ( elfheader ) ) {
printf ( " Error: not a valid PPC32 or PPC64 ELF file! \n \r " ) ;
exit ( ) ;
}
2006-09-19 14:05:08 +10:00
if ( platform_ops . image_hdr )
platform_ops . image_hdr ( elfheader ) ;
2005-11-16 13:38:21 +11:00
2006-09-19 14:05:08 +10:00
/* We need to alloc the memsize plus the file offset since gzip
2005-11-07 00:57:58 -08:00
* will expand the header ( file offset ) , then the kernel , then
* possible rubbish we don ' t care about . But the kernel bss must
* be claimed ( it will be zero ' d by the kernel itself )
*/
2005-10-28 17:46:40 -07:00
printf ( " Allocating 0x%lx bytes for kernel ... \n \r " , vmlinux . memsize ) ;
2006-09-19 14:05:08 +10:00
vmlinux . addr = ( unsigned long ) malloc ( vmlinux . memsize ) ;
2005-04-16 15:20:36 -07:00
if ( vmlinux . addr = = 0 ) {
printf ( " Can't allocate memory for kernel image ! \n \r " ) ;
exit ( ) ;
}
/*
2006-11-16 15:31:32 +11:00
* Now find the initrd
*
* First see if we have an image attached to us . If so
* allocate memory for it and copy it there .
2005-04-16 15:20:36 -07:00
*/
initrd . size = ( unsigned long ) ( _initrd_end - _initrd_start ) ;
initrd . memsize = initrd . size ;
2006-11-16 15:31:32 +11:00
if ( initrd . size > 0 ) {
2006-09-19 14:05:08 +10:00
printf ( " Allocating 0x%lx bytes for initrd ... \n \r " ,
initrd . size ) ;
initrd . addr = ( unsigned long ) malloc ( ( u32 ) initrd . size ) ;
2005-04-16 15:20:36 -07:00
if ( initrd . addr = = 0 ) {
2006-09-19 14:05:08 +10:00
printf ( " Can't allocate memory for initial "
" ramdisk ! \n \r " ) ;
2005-04-16 15:20:36 -07:00
exit ( ) ;
}
2006-09-19 14:05:08 +10:00
printf ( " initial ramdisk moving 0x%lx <- 0x%lx "
" (0x%lx bytes) \n \r " , initrd . addr ,
( unsigned long ) _initrd_start , initrd . size ) ;
memmove ( ( void * ) initrd . addr , ( void * ) _initrd_start ,
initrd . size ) ;
printf ( " initrd head: 0x%lx \n \r " ,
* ( ( unsigned long * ) initrd . addr ) ) ;
2006-11-16 15:31:32 +11:00
} else if ( a2 ! = 0 ) {
/* Otherwise, see if yaboot or another loader gave us an initrd */
initrd . addr = a1 ;
initrd . memsize = initrd . size = a2 ;
printf ( " Using loader supplied initrd at 0x%lx (0x%lx bytes) \n \r " ,
initrd . addr , initrd . size ) ;
2005-04-16 15:20:36 -07:00
}
/* Eventually gunzip the kernel */
if ( * ( unsigned short * ) vmlinuz . addr = = 0x1f8b ) {
printf ( " gunzipping (0x%lx <- 0x%lx:0x%0lx)... " ,
vmlinux . addr , vmlinuz . addr , vmlinuz . addr + vmlinuz . size ) ;
len = vmlinuz . size ;
2005-10-28 17:46:40 -07:00
gunzip ( ( void * ) vmlinux . addr , vmlinux . memsize ,
2005-04-16 15:20:36 -07:00
( unsigned char * ) vmlinuz . addr , & len ) ;
printf ( " done 0x%lx bytes \n \r " , len ) ;
} else {
2006-09-19 14:05:08 +10:00
memmove ( ( void * ) vmlinux . addr , ( void * ) vmlinuz . addr ,
vmlinuz . size ) ;
2005-04-16 15:20:36 -07:00
}
/* Skip over the ELF header */
# ifdef DEBUG
printf ( " ... skipping 0x%lx bytes of ELF header \n \r " ,
2005-11-16 13:38:21 +11:00
elfoffset ) ;
2005-04-16 15:20:36 -07:00
# endif
2005-11-16 13:38:21 +11:00
vmlinux . addr + = elfoffset ;
2005-04-16 15:20:36 -07:00
flush_cache ( ( void * ) vmlinux . addr , vmlinux . size ) ;
2006-09-19 14:05:08 +10:00
}
2005-04-16 15:20:36 -07:00
2006-09-19 14:05:08 +10:00
/* A buffer that may be edited by tools operating on a zImage binary so as to
* edit the command line passed to vmlinux ( by setting / chosen / bootargs ) .
* The buffer is put in it ' s own section so that tools may locate it easier .
*/
static char builtin_cmdline [ COMMAND_LINE_SIZE ]
__attribute__ ( ( __section__ ( " __builtin_cmdline " ) ) ) ;
2005-04-16 15:20:36 -07:00
2006-09-19 14:05:08 +10:00
static void get_cmdline ( char * buf , int size )
{
void * devp ;
int len = strlen ( builtin_cmdline ) ;
2005-04-16 15:20:36 -07:00
2006-09-19 14:05:08 +10:00
buf [ 0 ] = ' \0 ' ;
if ( len > 0 ) { /* builtin_cmdline overrides dt's /chosen/bootargs */
len = min ( len , size - 1 ) ;
strncpy ( buf , builtin_cmdline , len ) ;
buf [ len ] = ' \0 ' ;
}
else if ( ( devp = finddevice ( " /chosen " ) ) )
getprop ( devp , " bootargs " , buf , size ) ;
}
static void set_cmdline ( char * buf )
{
void * devp ;
if ( ( devp = finddevice ( " /chosen " ) ) )
setprop ( devp , " bootargs " , buf , strlen ( buf ) + 1 ) ;
2005-04-16 15:20:36 -07:00
}
2006-09-19 14:05:08 +10:00
struct platform_ops platform_ops ;
struct dt_ops dt_ops ;
struct console_ops console_ops ;
void start ( unsigned long a1 , unsigned long a2 , void * promptr , void * sp )
{
kernel_entry_t kentry ;
char cmdline [ COMMAND_LINE_SIZE ] ;
[POWERPC] Cleanup zImage handling of kernel entry with flat device tree
This makes 2 changes to clean up the flat device tree handling
logic in the zImage wrapper.
First, there were two callbacks from the dt_ops structure used for
producing a final flat tree to pass to the kerne: dt_ops.ft_pack()
which packed the flat tree (possibly a no-op) and dt_ops.ft_addr()
which retreived the address of the final blob. Since they were only
ever called together, this patch combines the two into a single new
callback, dt_ops.finalize(). This new callback does whatever
platform-dependent things are necessary to produce a final flat device
tree blob, and returns the blob's addres.
Second, the current logic calls the kernel with a flat device tree if
one is build into the zImage wrapper, otherwise it boots the kernel
with a PROM pointer, expecting the kernel to copy the OF device tree
itself. This approach precludes the possibility of the platform
wrapper code building a flat device tree from whatever
platform-specific information firmware provides. Thus, this patch
takes the more sensible approach of invoking the kernel with a flat
tree if the dt_ops.finalize callback provides one (by whatever means).
So, the dt_ops.finalize callback can be NULL, or can be a function
which returns NULL. In either case, the zImage wrapper logic assumes
that this is a platform with OF and invokes the kernel accordingly.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2006-11-21 11:37:37 +11:00
unsigned long ft_addr = 0 ;
2006-09-19 14:05:08 +10:00
memset ( __bss_start , 0 , _end - __bss_start ) ;
memset ( & platform_ops , 0 , sizeof ( platform_ops ) ) ;
memset ( & dt_ops , 0 , sizeof ( dt_ops ) ) ;
memset ( & console_ops , 0 , sizeof ( console_ops ) ) ;
2006-10-16 13:49:27 -07:00
if ( platform_init ( promptr , _dtb_start , _dtb_end ) )
2006-09-19 14:05:08 +10:00
exit ( ) ;
if ( console_ops . open & & ( console_ops . open ( ) < 0 ) )
exit ( ) ;
if ( platform_ops . fixups )
platform_ops . fixups ( ) ;
printf ( " \n \r zImage starting: loaded at 0x%p (sp: 0x%p) \n \r " ,
_start , sp ) ;
2006-11-16 15:31:32 +11:00
prep_kernel ( a1 , a2 ) ;
2006-09-19 14:05:08 +10:00
/* If cmdline came from zimage wrapper or if we can edit the one
* in the dt , print it out and edit it , if possible .
*/
if ( ( strlen ( builtin_cmdline ) > 0 ) | | console_ops . edit_cmdline ) {
get_cmdline ( cmdline , COMMAND_LINE_SIZE ) ;
printf ( " \n \r Linux/PowerPC load: %s " , cmdline ) ;
if ( console_ops . edit_cmdline )
console_ops . edit_cmdline ( cmdline , COMMAND_LINE_SIZE ) ;
printf ( " \n \r " ) ;
set_cmdline ( cmdline ) ;
}
[POWERPC] Cleanup zImage handling of kernel entry with flat device tree
This makes 2 changes to clean up the flat device tree handling
logic in the zImage wrapper.
First, there were two callbacks from the dt_ops structure used for
producing a final flat tree to pass to the kerne: dt_ops.ft_pack()
which packed the flat tree (possibly a no-op) and dt_ops.ft_addr()
which retreived the address of the final blob. Since they were only
ever called together, this patch combines the two into a single new
callback, dt_ops.finalize(). This new callback does whatever
platform-dependent things are necessary to produce a final flat device
tree blob, and returns the blob's addres.
Second, the current logic calls the kernel with a flat device tree if
one is build into the zImage wrapper, otherwise it boots the kernel
with a PROM pointer, expecting the kernel to copy the OF device tree
itself. This approach precludes the possibility of the platform
wrapper code building a flat device tree from whatever
platform-specific information firmware provides. Thus, this patch
takes the more sensible approach of invoking the kernel with a flat
tree if the dt_ops.finalize callback provides one (by whatever means).
So, the dt_ops.finalize callback can be NULL, or can be a function
which returns NULL. In either case, the zImage wrapper logic assumes
that this is a platform with OF and invokes the kernel accordingly.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2006-11-21 11:37:37 +11:00
printf ( " Finalizing device tree... " ) ;
if ( dt_ops . finalize )
ft_addr = dt_ops . finalize ( ) ;
if ( ft_addr )
printf ( " flat tree at 0x%lx \n \r " , ft_addr ) ;
else
printf ( " using OF tree (promptr=%p) \n \r " , promptr ) ;
2006-09-19 14:05:08 +10:00
if ( console_ops . close )
console_ops . close ( ) ;
kentry = ( kernel_entry_t ) vmlinux . addr ;
[POWERPC] Cleanup zImage handling of kernel entry with flat device tree
This makes 2 changes to clean up the flat device tree handling
logic in the zImage wrapper.
First, there were two callbacks from the dt_ops structure used for
producing a final flat tree to pass to the kerne: dt_ops.ft_pack()
which packed the flat tree (possibly a no-op) and dt_ops.ft_addr()
which retreived the address of the final blob. Since they were only
ever called together, this patch combines the two into a single new
callback, dt_ops.finalize(). This new callback does whatever
platform-dependent things are necessary to produce a final flat device
tree blob, and returns the blob's addres.
Second, the current logic calls the kernel with a flat device tree if
one is build into the zImage wrapper, otherwise it boots the kernel
with a PROM pointer, expecting the kernel to copy the OF device tree
itself. This approach precludes the possibility of the platform
wrapper code building a flat device tree from whatever
platform-specific information firmware provides. Thus, this patch
takes the more sensible approach of invoking the kernel with a flat
tree if the dt_ops.finalize callback provides one (by whatever means).
So, the dt_ops.finalize callback can be NULL, or can be a function
which returns NULL. In either case, the zImage wrapper logic assumes
that this is a platform with OF and invokes the kernel accordingly.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2006-11-21 11:37:37 +11:00
if ( ft_addr )
kentry ( ft_addr , 0 , NULL ) ;
2006-09-19 14:05:08 +10:00
else
/* XXX initrd addr/size should be passed in properties */
2006-11-16 15:31:32 +11:00
kentry ( initrd . addr , initrd . size , promptr ) ;
2006-09-19 14:05:08 +10:00
/* console closed so printf below may not work */
printf ( " Error: Linux kernel returned to zImage boot wrapper! \n \r " ) ;
exit ( ) ;
}