2007-06-13 14:52:54 +10:00
/*
* Copyright ( C ) Paul Mackerras 1997.
*
* 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 .
*/
# include <stddef.h>
# include "types.h"
# include "elf.h"
# include "string.h"
# include "stdio.h"
# include "page.h"
# include "ops.h"
# include "of.h"
2014-04-24 09:23:27 +02:00
typedef u32 prom_arg_t ;
2014-04-24 09:23:26 +02:00
/* The following structure is used to communicate with open firmware.
* All arguments in and out are in big endian format . */
struct prom_args {
__be32 service ; /* Address of service name string. */
__be32 nargs ; /* Number of input arguments. */
__be32 nret ; /* Number of output arguments. */
__be32 args [ 10 ] ; /* Input/output arguments. */
} ;
2014-04-24 09:23:36 +02:00
# ifdef __powerpc64__
extern int prom ( void * ) ;
# else
2007-06-13 14:52:54 +10:00
static int ( * prom ) ( void * ) ;
2014-04-24 09:23:36 +02:00
# endif
2007-06-13 14:52:54 +10:00
void of_init ( void * promptr )
{
2014-04-24 09:23:36 +02:00
# ifndef __powerpc64__
2007-06-13 14:52:54 +10:00
prom = ( int ( * ) ( void * ) ) promptr ;
2014-04-24 09:23:36 +02:00
# endif
2007-06-13 14:52:54 +10:00
}
2014-04-24 09:23:26 +02:00
# define ADDR(x) (u32)(unsigned long)(x)
2007-06-13 14:52:54 +10:00
int of_call_prom ( const char * service , int nargs , int nret , . . . )
{
int i ;
2014-04-24 09:23:26 +02:00
struct prom_args args ;
2007-06-13 14:52:54 +10:00
va_list list ;
2014-04-24 09:23:28 +02:00
args . service = cpu_to_be32 ( ADDR ( service ) ) ;
args . nargs = cpu_to_be32 ( nargs ) ;
args . nret = cpu_to_be32 ( nret ) ;
2007-06-13 14:52:54 +10:00
va_start ( list , nret ) ;
for ( i = 0 ; i < nargs ; i + + )
2014-04-24 09:23:28 +02:00
args . args [ i ] = cpu_to_be32 ( va_arg ( list , prom_arg_t ) ) ;
2007-06-13 14:52:54 +10:00
va_end ( list ) ;
for ( i = 0 ; i < nret ; i + + )
args . args [ nargs + i ] = 0 ;
if ( prom ( & args ) < 0 )
2014-04-24 09:23:29 +02:00
return PROM_ERROR ;
2007-06-13 14:52:54 +10:00
2014-04-24 09:23:28 +02:00
return ( nret > 0 ) ? be32_to_cpu ( args . args [ nargs ] ) : 0 ;
2007-06-13 14:52:54 +10:00
}
static int of_call_prom_ret ( const char * service , int nargs , int nret ,
2014-04-24 09:23:27 +02:00
prom_arg_t * rets , . . . )
2007-06-13 14:52:54 +10:00
{
int i ;
2014-04-24 09:23:26 +02:00
struct prom_args args ;
2007-06-13 14:52:54 +10:00
va_list list ;
2014-04-24 09:23:28 +02:00
args . service = cpu_to_be32 ( ADDR ( service ) ) ;
args . nargs = cpu_to_be32 ( nargs ) ;
args . nret = cpu_to_be32 ( nret ) ;
2007-06-13 14:52:54 +10:00
va_start ( list , rets ) ;
for ( i = 0 ; i < nargs ; i + + )
2014-04-24 09:23:28 +02:00
args . args [ i ] = cpu_to_be32 ( va_arg ( list , prom_arg_t ) ) ;
2007-06-13 14:52:54 +10:00
va_end ( list ) ;
for ( i = 0 ; i < nret ; i + + )
args . args [ nargs + i ] = 0 ;
if ( prom ( & args ) < 0 )
2014-04-24 09:23:29 +02:00
return PROM_ERROR ;
2007-06-13 14:52:54 +10:00
2014-04-24 09:23:29 +02:00
if ( rets ! = NULL )
2007-06-13 14:52:54 +10:00
for ( i = 1 ; i < nret ; + + i )
2014-04-24 09:23:28 +02:00
rets [ i - 1 ] = be32_to_cpu ( args . args [ nargs + i ] ) ;
2007-06-13 14:52:54 +10:00
2014-04-24 09:23:28 +02:00
return ( nret > 0 ) ? be32_to_cpu ( args . args [ nargs ] ) : 0 ;
2007-06-13 14:52:54 +10:00
}
/* returns true if s2 is a prefix of s1 */
static int string_match ( const char * s1 , const char * s2 )
{
for ( ; * s2 ; + + s2 )
if ( * s1 + + ! = * s2 )
return 0 ;
return 1 ;
}
/*
* Older OF ' s require that when claiming a specific range of addresses ,
* we claim the physical space in the / memory node and the virtual
* space in the chosen mmu node , and then do a map operation to
* map virtual to physical .
*/
static int need_map = - 1 ;
static ihandle chosen_mmu ;
2014-04-24 09:23:31 +02:00
static ihandle memory ;
2007-06-13 14:52:54 +10:00
static int check_of_version ( void )
{
phandle oprom , chosen ;
char version [ 64 ] ;
2007-06-27 16:54:58 +10:00
oprom = of_finddevice ( " /openprom " ) ;
2007-06-13 14:52:54 +10:00
if ( oprom = = ( phandle ) - 1 )
return 0 ;
2007-06-27 16:54:58 +10:00
if ( of_getprop ( oprom , " model " , version , sizeof ( version ) ) < = 0 )
2007-06-13 14:52:54 +10:00
return 0 ;
version [ sizeof ( version ) - 1 ] = 0 ;
printf ( " OF version = '%s' \r \n " , version ) ;
if ( ! string_match ( version , " Open Firmware, 1. " )
& & ! string_match ( version , " FirmWorks,3. " ) )
return 0 ;
2007-06-27 16:54:58 +10:00
chosen = of_finddevice ( " /chosen " ) ;
2007-06-13 14:52:54 +10:00
if ( chosen = = ( phandle ) - 1 ) {
2007-06-27 16:54:58 +10:00
chosen = of_finddevice ( " /chosen@0 " ) ;
2007-06-13 14:52:54 +10:00
if ( chosen = = ( phandle ) - 1 ) {
printf ( " no chosen \n " ) ;
return 0 ;
}
}
2007-06-27 16:54:58 +10:00
if ( of_getprop ( chosen , " mmu " , & chosen_mmu , sizeof ( chosen_mmu ) ) < = 0 ) {
2007-06-13 14:52:54 +10:00
printf ( " no mmu \n " ) ;
return 0 ;
}
2014-04-24 09:23:31 +02:00
memory = of_call_prom ( " open " , 1 , 1 , " /memory " ) ;
if ( memory = = PROM_ERROR ) {
memory = of_call_prom ( " open " , 1 , 1 , " /memory@0 " ) ;
if ( memory = = PROM_ERROR ) {
2007-06-13 14:52:54 +10:00
printf ( " no memory node \n " ) ;
return 0 ;
}
}
printf ( " old OF detected \r \n " ) ;
return 1 ;
}
2014-04-24 09:23:30 +02:00
unsigned int of_claim ( unsigned long virt , unsigned long size ,
unsigned long align )
2007-06-13 14:52:54 +10:00
{
int ret ;
2014-04-24 09:23:27 +02:00
prom_arg_t result ;
2007-06-13 14:52:54 +10:00
if ( need_map < 0 )
need_map = check_of_version ( ) ;
if ( align | | ! need_map )
2014-04-24 09:23:30 +02:00
return of_call_prom ( " claim " , 3 , 1 , virt , size , align ) ;
2007-06-13 14:52:54 +10:00
ret = of_call_prom_ret ( " call-method " , 5 , 2 , & result , " claim " , memory ,
align , size , virt ) ;
if ( ret ! = 0 | | result = = - 1 )
2014-04-24 09:23:30 +02:00
return - 1 ;
2007-06-13 14:52:54 +10:00
ret = of_call_prom_ret ( " call-method " , 5 , 2 , & result , " claim " , chosen_mmu ,
align , size , virt ) ;
/* 0x12 == coherent + read/write */
ret = of_call_prom ( " call-method " , 6 , 1 , " map " , chosen_mmu ,
0x12 , size , virt , virt ) ;
2014-04-24 09:23:30 +02:00
return virt ;
2007-06-13 14:52:54 +10:00
}
2007-06-27 16:54:58 +10:00
void * of_vmlinux_alloc ( unsigned long size )
{
2008-06-24 14:20:29 +10:00
unsigned long start = ( unsigned long ) _start , end = ( unsigned long ) _end ;
2014-04-24 09:23:30 +02:00
unsigned long addr ;
2008-06-24 14:20:29 +10:00
void * p ;
/* With some older POWER4 firmware we need to claim the area the kernel
* will reside in . Newer firmwares don ' t need this so we just ignore
* the return value .
*/
2014-04-24 09:23:30 +02:00
addr = ( unsigned long ) of_claim ( start , end - start , 0 ) ;
printf ( " Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx \r \n " ,
2008-06-24 14:20:29 +10:00
start , end , end - start , addr ) ;
p = malloc ( size ) ;
2007-06-27 16:54:58 +10:00
if ( ! p )
fatal ( " Can't allocate memory for kernel image! \n \r " ) ;
return p ;
}
2007-06-13 14:52:54 +10:00
void of_exit ( void )
{
of_call_prom ( " exit " , 0 , 0 ) ;
}
2007-06-27 16:54:58 +10:00
/*
* OF device tree routines
*/
void * of_finddevice ( const char * name )
{
2014-04-24 09:23:32 +02:00
return ( void * ) ( unsigned long ) of_call_prom ( " finddevice " , 1 , 1 , name ) ;
2007-06-27 16:54:58 +10:00
}
int of_getprop ( const void * phandle , const char * name , void * buf ,
const int buflen )
{
return of_call_prom ( " getprop " , 4 , 1 , phandle , name , buf , buflen ) ;
}
int of_setprop ( const void * phandle , const char * name , const void * buf ,
const int buflen )
{
return of_call_prom ( " setprop " , 4 , 1 , phandle , name , buf , buflen ) ;
}