2007-12-18 07:06:42 +03:00
/*
* ( C ) Copyright David Gibson < dwg @ au1 . ibm . com > , IBM Corporation . 2005.
*
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*/
# include "dtc.h"
2008-08-07 06:24:17 +04:00
# include "srcpos.h"
2007-12-18 07:06:42 +03:00
# define FTF_FULLPATH 0x1
# define FTF_VARALIGN 0x2
# define FTF_NAMEPROPS 0x4
# define FTF_BOOTCPUID 0x8
# define FTF_STRTABSIZE 0x10
# define FTF_STRUCTSIZE 0x20
# define FTF_NOPS 0x40
static struct version_info {
int version ;
int last_comp_version ;
int hdr_size ;
int flags ;
} version_table [ ] = {
{ 1 , 1 , FDT_V1_SIZE ,
FTF_FULLPATH | FTF_VARALIGN | FTF_NAMEPROPS } ,
{ 2 , 1 , FDT_V2_SIZE ,
FTF_FULLPATH | FTF_VARALIGN | FTF_NAMEPROPS | FTF_BOOTCPUID } ,
{ 3 , 1 , FDT_V3_SIZE ,
FTF_FULLPATH | FTF_VARALIGN | FTF_NAMEPROPS | FTF_BOOTCPUID | FTF_STRTABSIZE } ,
{ 16 , 16 , FDT_V3_SIZE ,
FTF_BOOTCPUID | FTF_STRTABSIZE | FTF_NOPS } ,
{ 17 , 16 , FDT_V17_SIZE ,
FTF_BOOTCPUID | FTF_STRTABSIZE | FTF_STRUCTSIZE | FTF_NOPS } ,
} ;
struct emitter {
void ( * cell ) ( void * , cell_t ) ;
void ( * string ) ( void * , char * , int ) ;
void ( * align ) ( void * , int ) ;
void ( * data ) ( void * , struct data ) ;
2010-11-18 02:28:20 +03:00
void ( * beginnode ) ( void * , struct label * labels ) ;
void ( * endnode ) ( void * , struct label * labels ) ;
void ( * property ) ( void * , struct label * labels ) ;
2007-12-18 07:06:42 +03:00
} ;
static void bin_emit_cell ( void * e , cell_t val )
{
struct data * dtbuf = e ;
* dtbuf = data_append_cell ( * dtbuf , val ) ;
}
static void bin_emit_string ( void * e , char * str , int len )
{
struct data * dtbuf = e ;
if ( len = = 0 )
len = strlen ( str ) ;
* dtbuf = data_append_data ( * dtbuf , str , len ) ;
* dtbuf = data_append_byte ( * dtbuf , ' \0 ' ) ;
}
static void bin_emit_align ( void * e , int a )
{
struct data * dtbuf = e ;
* dtbuf = data_append_align ( * dtbuf , a ) ;
}
static void bin_emit_data ( void * e , struct data d )
{
struct data * dtbuf = e ;
* dtbuf = data_append_data ( * dtbuf , d . val , d . len ) ;
}
2010-11-18 02:28:20 +03:00
static void bin_emit_beginnode ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
bin_emit_cell ( e , FDT_BEGIN_NODE ) ;
}
2010-11-18 02:28:20 +03:00
static void bin_emit_endnode ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
bin_emit_cell ( e , FDT_END_NODE ) ;
}
2010-11-18 02:28:20 +03:00
static void bin_emit_property ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
bin_emit_cell ( e , FDT_PROP ) ;
}
static struct emitter bin_emitter = {
. cell = bin_emit_cell ,
. string = bin_emit_string ,
. align = bin_emit_align ,
. data = bin_emit_data ,
. beginnode = bin_emit_beginnode ,
. endnode = bin_emit_endnode ,
. property = bin_emit_property ,
} ;
static void emit_label ( FILE * f , const char * prefix , const char * label )
{
fprintf ( f , " \t .globl \t %s_%s \n " , prefix , label ) ;
fprintf ( f , " %s_%s: \n " , prefix , label ) ;
fprintf ( f , " _%s_%s: \n " , prefix , label ) ;
}
static void emit_offset_label ( FILE * f , const char * label , int offset )
{
fprintf ( f , " \t .globl \t %s \n " , label ) ;
fprintf ( f , " %s \t = . + %d \n " , label , offset ) ;
}
2010-11-18 02:28:20 +03:00
# define ASM_EMIT_BELONG(f, fmt, ...) \
{ \
fprintf ( ( f ) , " \t .byte \t (( " fmt " ) >> 24) & 0xff \n " , __VA_ARGS__ ) ; \
fprintf ( ( f ) , " \t .byte \t (( " fmt " ) >> 16) & 0xff \n " , __VA_ARGS__ ) ; \
fprintf ( ( f ) , " \t .byte \t (( " fmt " ) >> 8) & 0xff \n " , __VA_ARGS__ ) ; \
fprintf ( ( f ) , " \t .byte \t ( " fmt " ) & 0xff \n " , __VA_ARGS__ ) ; \
}
2007-12-18 07:06:42 +03:00
static void asm_emit_cell ( void * e , cell_t val )
{
FILE * f = e ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t .byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x \n " ,
( val > > 24 ) & 0xff , ( val > > 16 ) & 0xff ,
( val > > 8 ) & 0xff , val & 0xff ) ;
2007-12-18 07:06:42 +03:00
}
static void asm_emit_string ( void * e , char * str , int len )
{
FILE * f = e ;
char c = 0 ;
if ( len ! = 0 ) {
/* XXX: ewww */
c = str [ len ] ;
str [ len ] = ' \0 ' ;
}
fprintf ( f , " \t .string \t \" %s \" \n " , str ) ;
if ( len ! = 0 ) {
str [ len ] = c ;
}
}
static void asm_emit_align ( void * e , int a )
{
FILE * f = e ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t .balign \t %d, 0 \n " , a ) ;
2007-12-18 07:06:42 +03:00
}
static void asm_emit_data ( void * e , struct data d )
{
FILE * f = e ;
int off = 0 ;
2008-08-07 06:24:17 +04:00
struct marker * m = d . markers ;
2007-12-18 07:06:42 +03:00
2008-08-07 06:24:17 +04:00
for_each_marker_of_type ( m , LABEL )
emit_offset_label ( f , m - > ref , m - > offset ) ;
2007-12-18 07:06:42 +03:00
2008-08-07 06:24:17 +04:00
while ( ( d . len - off ) > = sizeof ( uint32_t ) ) {
2010-11-18 02:28:20 +03:00
asm_emit_cell ( e , fdt32_to_cpu ( * ( ( uint32_t * ) ( d . val + off ) ) ) ) ;
2008-08-07 06:24:17 +04:00
off + = sizeof ( uint32_t ) ;
2007-12-18 07:06:42 +03:00
}
2008-08-07 06:24:17 +04:00
while ( ( d . len - off ) > = 1 ) {
2007-12-18 07:06:42 +03:00
fprintf ( f , " \t .byte \t 0x%hhx \n " , d . val [ off ] ) ;
off + = 1 ;
}
assert ( off = = d . len ) ;
}
2010-11-18 02:28:20 +03:00
static void asm_emit_beginnode ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
FILE * f = e ;
2010-11-18 02:28:20 +03:00
struct label * l ;
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
for_each_label ( labels , l ) {
fprintf ( f , " \t .globl \t %s \n " , l - > label ) ;
fprintf ( f , " %s: \n " , l - > label ) ;
2007-12-18 07:06:42 +03:00
}
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* FDT_BEGIN_NODE */ \n " ) ;
asm_emit_cell ( e , FDT_BEGIN_NODE ) ;
2007-12-18 07:06:42 +03:00
}
2010-11-18 02:28:20 +03:00
static void asm_emit_endnode ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
FILE * f = e ;
2010-11-18 02:28:20 +03:00
struct label * l ;
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* FDT_END_NODE */ \n " ) ;
asm_emit_cell ( e , FDT_END_NODE ) ;
for_each_label ( labels , l ) {
fprintf ( f , " \t .globl \t %s_end \n " , l - > label ) ;
fprintf ( f , " %s_end: \n " , l - > label ) ;
2007-12-18 07:06:42 +03:00
}
}
2010-11-18 02:28:20 +03:00
static void asm_emit_property ( void * e , struct label * labels )
2007-12-18 07:06:42 +03:00
{
FILE * f = e ;
2010-11-18 02:28:20 +03:00
struct label * l ;
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
for_each_label ( labels , l ) {
fprintf ( f , " \t .globl \t %s \n " , l - > label ) ;
fprintf ( f , " %s: \n " , l - > label ) ;
2007-12-18 07:06:42 +03:00
}
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* FDT_PROP */ \n " ) ;
asm_emit_cell ( e , FDT_PROP ) ;
2007-12-18 07:06:42 +03:00
}
static struct emitter asm_emitter = {
. cell = asm_emit_cell ,
. string = asm_emit_string ,
. align = asm_emit_align ,
. data = asm_emit_data ,
. beginnode = asm_emit_beginnode ,
. endnode = asm_emit_endnode ,
. property = asm_emit_property ,
} ;
static int stringtable_insert ( struct data * d , const char * str )
{
int i ;
/* FIXME: do this more efficiently? */
for ( i = 0 ; i < d - > len ; i + + ) {
if ( streq ( str , d - > val + i ) )
return i ;
}
* d = data_append_data ( * d , str , strlen ( str ) + 1 ) ;
return i ;
}
static void flatten_tree ( struct node * tree , struct emitter * emit ,
void * etarget , struct data * strbuf ,
struct version_info * vi )
{
struct property * prop ;
struct node * child ;
int seen_name_prop = 0 ;
2012-09-29 01:25:59 +04:00
if ( tree - > deleted )
return ;
2010-11-18 02:28:20 +03:00
emit - > beginnode ( etarget , tree - > labels ) ;
2007-12-18 07:06:42 +03:00
if ( vi - > flags & FTF_FULLPATH )
emit - > string ( etarget , tree - > fullpath , 0 ) ;
else
emit - > string ( etarget , tree - > name , 0 ) ;
emit - > align ( etarget , sizeof ( cell_t ) ) ;
for_each_property ( tree , prop ) {
int nameoff ;
if ( streq ( prop - > name , " name " ) )
seen_name_prop = 1 ;
nameoff = stringtable_insert ( strbuf , prop - > name ) ;
2010-11-18 02:28:20 +03:00
emit - > property ( etarget , prop - > labels ) ;
2007-12-18 07:06:42 +03:00
emit - > cell ( etarget , prop - > val . len ) ;
emit - > cell ( etarget , nameoff ) ;
if ( ( vi - > flags & FTF_VARALIGN ) & & ( prop - > val . len > = 8 ) )
emit - > align ( etarget , 8 ) ;
emit - > data ( etarget , prop - > val ) ;
emit - > align ( etarget , sizeof ( cell_t ) ) ;
}
if ( ( vi - > flags & FTF_NAMEPROPS ) & & ! seen_name_prop ) {
emit - > property ( etarget , NULL ) ;
emit - > cell ( etarget , tree - > basenamelen + 1 ) ;
emit - > cell ( etarget , stringtable_insert ( strbuf , " name " ) ) ;
if ( ( vi - > flags & FTF_VARALIGN ) & & ( ( tree - > basenamelen + 1 ) > = 8 ) )
emit - > align ( etarget , 8 ) ;
emit - > string ( etarget , tree - > name , tree - > basenamelen ) ;
emit - > align ( etarget , sizeof ( cell_t ) ) ;
}
for_each_child ( tree , child ) {
flatten_tree ( child , emit , etarget , strbuf , vi ) ;
}
2010-11-18 02:28:20 +03:00
emit - > endnode ( etarget , tree - > labels ) ;
2007-12-18 07:06:42 +03:00
}
static struct data flatten_reserve_list ( struct reserve_info * reservelist ,
struct version_info * vi )
{
struct reserve_info * re ;
struct data d = empty_data ;
static struct fdt_reserve_entry null_re = { 0 , 0 } ;
int j ;
for ( re = reservelist ; re ; re = re - > next ) {
d = data_append_re ( d , & re - > re ) ;
}
/*
* Add additional reserved slots if the user asked for them .
*/
for ( j = 0 ; j < reservenum ; j + + ) {
d = data_append_re ( d , & null_re ) ;
}
return d ;
}
static void make_fdt_header ( struct fdt_header * fdt ,
struct version_info * vi ,
int reservesize , int dtsize , int strsize ,
int boot_cpuid_phys )
{
int reserve_off ;
reservesize + = sizeof ( struct fdt_reserve_entry ) ;
memset ( fdt , 0xff , sizeof ( * fdt ) ) ;
2008-08-07 06:24:17 +04:00
fdt - > magic = cpu_to_fdt32 ( FDT_MAGIC ) ;
fdt - > version = cpu_to_fdt32 ( vi - > version ) ;
fdt - > last_comp_version = cpu_to_fdt32 ( vi - > last_comp_version ) ;
2007-12-18 07:06:42 +03:00
/* Reserve map should be doubleword aligned */
reserve_off = ALIGN ( vi - > hdr_size , 8 ) ;
2008-08-07 06:24:17 +04:00
fdt - > off_mem_rsvmap = cpu_to_fdt32 ( reserve_off ) ;
fdt - > off_dt_struct = cpu_to_fdt32 ( reserve_off + reservesize ) ;
fdt - > off_dt_strings = cpu_to_fdt32 ( reserve_off + reservesize
2007-12-18 07:06:42 +03:00
+ dtsize ) ;
2008-08-07 06:24:17 +04:00
fdt - > totalsize = cpu_to_fdt32 ( reserve_off + reservesize + dtsize + strsize ) ;
2007-12-18 07:06:42 +03:00
if ( vi - > flags & FTF_BOOTCPUID )
2008-08-07 06:24:17 +04:00
fdt - > boot_cpuid_phys = cpu_to_fdt32 ( boot_cpuid_phys ) ;
2007-12-18 07:06:42 +03:00
if ( vi - > flags & FTF_STRTABSIZE )
2008-08-07 06:24:17 +04:00
fdt - > size_dt_strings = cpu_to_fdt32 ( strsize ) ;
2007-12-18 07:06:42 +03:00
if ( vi - > flags & FTF_STRUCTSIZE )
2008-08-07 06:24:17 +04:00
fdt - > size_dt_struct = cpu_to_fdt32 ( dtsize ) ;
2007-12-18 07:06:42 +03:00
}
2008-08-07 06:24:17 +04:00
void dt_to_blob ( FILE * f , struct boot_info * bi , int version )
2007-12-18 07:06:42 +03:00
{
struct version_info * vi = NULL ;
int i ;
struct data blob = empty_data ;
struct data reservebuf = empty_data ;
struct data dtbuf = empty_data ;
struct data strbuf = empty_data ;
struct fdt_header fdt ;
int padlen = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( version_table ) ; i + + ) {
if ( version_table [ i ] . version = = version )
vi = & version_table [ i ] ;
}
if ( ! vi )
die ( " Unknown device tree blob version %d \n " , version ) ;
flatten_tree ( bi - > dt , & bin_emitter , & dtbuf , & strbuf , vi ) ;
bin_emit_cell ( & dtbuf , FDT_END ) ;
reservebuf = flatten_reserve_list ( bi - > reservelist , vi ) ;
/* Make header */
make_fdt_header ( & fdt , vi , reservebuf . len , dtbuf . len , strbuf . len ,
2008-08-07 06:24:17 +04:00
bi - > boot_cpuid_phys ) ;
2007-12-18 07:06:42 +03:00
/*
* If the user asked for more space than is used , adjust the totalsize .
*/
if ( minsize > 0 ) {
2008-08-07 06:24:17 +04:00
padlen = minsize - fdt32_to_cpu ( fdt . totalsize ) ;
2007-12-18 07:06:42 +03:00
if ( ( padlen < 0 ) & & ( quiet < 1 ) )
fprintf ( stderr ,
" Warning: blob size %d >= minimum size %d \n " ,
2008-08-07 06:24:17 +04:00
fdt32_to_cpu ( fdt . totalsize ) , minsize ) ;
2007-12-18 07:06:42 +03:00
}
if ( padsize > 0 )
padlen = padsize ;
if ( padlen > 0 ) {
2008-08-07 06:24:17 +04:00
int tsize = fdt32_to_cpu ( fdt . totalsize ) ;
2007-12-18 07:06:42 +03:00
tsize + = padlen ;
2008-08-07 06:24:17 +04:00
fdt . totalsize = cpu_to_fdt32 ( tsize ) ;
2007-12-18 07:06:42 +03:00
}
/*
* Assemble the blob : start with the header , add with alignment
* the reserve buffer , add the reserve map terminating zeroes ,
* the device tree itself , and finally the strings .
*/
2008-08-07 06:24:17 +04:00
blob = data_append_data ( blob , & fdt , vi - > hdr_size ) ;
2007-12-18 07:06:42 +03:00
blob = data_append_align ( blob , 8 ) ;
blob = data_merge ( blob , reservebuf ) ;
blob = data_append_zeroes ( blob , sizeof ( struct fdt_reserve_entry ) ) ;
blob = data_merge ( blob , dtbuf ) ;
blob = data_merge ( blob , strbuf ) ;
/*
* If the user asked for more space than is used , pad out the blob .
*/
if ( padlen > 0 )
blob = data_append_zeroes ( blob , padlen ) ;
2010-11-18 02:28:20 +03:00
if ( fwrite ( blob . val , blob . len , 1 , f ) ! = 1 ) {
if ( ferror ( f ) )
die ( " Error writing device tree blob: %s \n " ,
strerror ( errno ) ) ;
else
die ( " Short write on device tree blob \n " ) ;
}
2007-12-18 07:06:42 +03:00
/*
* data_merge ( ) frees the right - hand element so only the blob
* remains to be freed .
*/
data_free ( blob ) ;
}
static void dump_stringtable_asm ( FILE * f , struct data strbuf )
{
const char * p ;
int len ;
p = strbuf . val ;
while ( p < ( strbuf . val + strbuf . len ) ) {
len = strlen ( p ) ;
fprintf ( f , " \t .string \" %s \" \n " , p ) ;
p + = len + 1 ;
}
}
2008-08-07 06:24:17 +04:00
void dt_to_asm ( FILE * f , struct boot_info * bi , int version )
2007-12-18 07:06:42 +03:00
{
struct version_info * vi = NULL ;
int i ;
struct data strbuf = empty_data ;
struct reserve_info * re ;
const char * symprefix = " dt " ;
for ( i = 0 ; i < ARRAY_SIZE ( version_table ) ; i + + ) {
if ( version_table [ i ] . version = = version )
vi = & version_table [ i ] ;
}
if ( ! vi )
die ( " Unknown device tree blob version %d \n " , version ) ;
fprintf ( f , " /* autogenerated by dtc, do not edit */ \n \n " ) ;
emit_label ( f , symprefix , " blob_start " ) ;
emit_label ( f , symprefix , " header " ) ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* magic */ \n " ) ;
asm_emit_cell ( f , FDT_MAGIC ) ;
fprintf ( f , " \t /* totalsize */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_blob_abs_end - _%s_blob_start " ,
symprefix , symprefix ) ;
fprintf ( f , " \t /* off_dt_struct */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_struct_start - _%s_blob_start " ,
2007-12-18 07:06:42 +03:00
symprefix , symprefix ) ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* off_dt_strings */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_strings_start - _%s_blob_start " ,
2007-12-18 07:06:42 +03:00
symprefix , symprefix ) ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* off_mem_rsvmap */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_reserve_map - _%s_blob_start " ,
2007-12-18 07:06:42 +03:00
symprefix , symprefix ) ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* version */ \n " ) ;
asm_emit_cell ( f , vi - > version ) ;
fprintf ( f , " \t /* last_comp_version */ \n " ) ;
asm_emit_cell ( f , vi - > last_comp_version ) ;
if ( vi - > flags & FTF_BOOTCPUID ) {
fprintf ( f , " \t /* boot_cpuid_phys */ \n " ) ;
asm_emit_cell ( f , bi - > boot_cpuid_phys ) ;
}
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
if ( vi - > flags & FTF_STRTABSIZE ) {
fprintf ( f , " \t /* size_dt_strings */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_strings_end - _%s_strings_start " ,
symprefix , symprefix ) ;
}
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
if ( vi - > flags & FTF_STRUCTSIZE ) {
fprintf ( f , " \t /* size_dt_struct */ \n " ) ;
ASM_EMIT_BELONG ( f , " _%s_struct_end - _%s_struct_start " ,
2007-12-18 07:06:42 +03:00
symprefix , symprefix ) ;
2010-11-18 02:28:20 +03:00
}
2007-12-18 07:06:42 +03:00
/*
* Reserve map entries .
* Align the reserve map to a doubleword boundary .
* Each entry is an ( address , size ) pair of u64 values .
* Always supply a zero - sized temination entry .
*/
asm_emit_align ( f , 8 ) ;
emit_label ( f , symprefix , " reserve_map " ) ;
fprintf ( f , " /* Memory reserve map from source file */ \n " ) ;
/*
* Use . long on high and low halfs of u64s to avoid . quad
* as it appears . quad isn ' t available in some assemblers .
*/
for ( re = bi - > reservelist ; re ; re = re - > next ) {
2010-11-18 02:28:20 +03:00
struct label * l ;
for_each_label ( re - > labels , l ) {
fprintf ( f , " \t .globl \t %s \n " , l - > label ) ;
fprintf ( f , " %s: \n " , l - > label ) ;
2007-12-18 07:06:42 +03:00
}
2010-11-18 02:28:20 +03:00
ASM_EMIT_BELONG ( f , " 0x%08x " , ( unsigned int ) ( re - > re . address > > 32 ) ) ;
ASM_EMIT_BELONG ( f , " 0x%08x " ,
( unsigned int ) ( re - > re . address & 0xffffffff ) ) ;
ASM_EMIT_BELONG ( f , " 0x%08x " , ( unsigned int ) ( re - > re . size > > 32 ) ) ;
ASM_EMIT_BELONG ( f , " 0x%08x " , ( unsigned int ) ( re - > re . size & 0xffffffff ) ) ;
2007-12-18 07:06:42 +03:00
}
for ( i = 0 ; i < reservenum ; i + + ) {
fprintf ( f , " \t .long \t 0, 0 \n \t .long \t 0, 0 \n " ) ;
}
fprintf ( f , " \t .long \t 0, 0 \n \t .long \t 0, 0 \n " ) ;
emit_label ( f , symprefix , " struct_start " ) ;
flatten_tree ( bi - > dt , & asm_emitter , f , & strbuf , vi ) ;
2010-11-18 02:28:20 +03:00
fprintf ( f , " \t /* FDT_END */ \n " ) ;
asm_emit_cell ( f , FDT_END ) ;
2007-12-18 07:06:42 +03:00
emit_label ( f , symprefix , " struct_end " ) ;
emit_label ( f , symprefix , " strings_start " ) ;
dump_stringtable_asm ( f , strbuf ) ;
emit_label ( f , symprefix , " strings_end " ) ;
emit_label ( f , symprefix , " blob_end " ) ;
/*
* If the user asked for more space than is used , pad it out .
*/
if ( minsize > 0 ) {
fprintf ( f , " \t .space \t %d - (_%s_blob_end - _%s_blob_start), 0 \n " ,
minsize , symprefix , symprefix ) ;
}
if ( padsize > 0 ) {
fprintf ( f , " \t .space \t %d, 0 \n " , padsize ) ;
}
emit_label ( f , symprefix , " blob_abs_end " ) ;
data_free ( strbuf ) ;
}
struct inbuf {
char * base , * limit , * ptr ;
} ;
static void inbuf_init ( struct inbuf * inb , void * base , void * limit )
{
inb - > base = base ;
inb - > limit = limit ;
inb - > ptr = inb - > base ;
}
static void flat_read_chunk ( struct inbuf * inb , void * p , int len )
{
if ( ( inb - > ptr + len ) > inb - > limit )
die ( " Premature end of data parsing flat device tree \n " ) ;
memcpy ( p , inb - > ptr , len ) ;
inb - > ptr + = len ;
}
2008-08-07 06:24:17 +04:00
static uint32_t flat_read_word ( struct inbuf * inb )
2007-12-18 07:06:42 +03:00
{
2008-08-07 06:24:17 +04:00
uint32_t val ;
2007-12-18 07:06:42 +03:00
assert ( ( ( inb - > ptr - inb - > base ) % sizeof ( val ) ) = = 0 ) ;
flat_read_chunk ( inb , & val , sizeof ( val ) ) ;
2008-08-07 06:24:17 +04:00
return fdt32_to_cpu ( val ) ;
2007-12-18 07:06:42 +03:00
}
static void flat_realign ( struct inbuf * inb , int align )
{
int off = inb - > ptr - inb - > base ;
inb - > ptr = inb - > base + ALIGN ( off , align ) ;
if ( inb - > ptr > inb - > limit )
die ( " Premature end of data parsing flat device tree \n " ) ;
}
static char * flat_read_string ( struct inbuf * inb )
{
int len = 0 ;
const char * p = inb - > ptr ;
char * str ;
do {
if ( p > = inb - > limit )
die ( " Premature end of data parsing flat device tree \n " ) ;
len + + ;
} while ( ( * p + + ) ! = ' \0 ' ) ;
2010-11-18 02:28:20 +03:00
str = xstrdup ( inb - > ptr ) ;
2007-12-18 07:06:42 +03:00
inb - > ptr + = len ;
2008-08-07 06:24:17 +04:00
flat_realign ( inb , sizeof ( uint32_t ) ) ;
2007-12-18 07:06:42 +03:00
return str ;
}
static struct data flat_read_data ( struct inbuf * inb , int len )
{
struct data d = empty_data ;
if ( len = = 0 )
return empty_data ;
d = data_grow_for ( d , len ) ;
d . len = len ;
flat_read_chunk ( inb , d . val , len ) ;
2008-08-07 06:24:17 +04:00
flat_realign ( inb , sizeof ( uint32_t ) ) ;
2007-12-18 07:06:42 +03:00
return d ;
}
static char * flat_read_stringtable ( struct inbuf * inb , int offset )
{
const char * p ;
p = inb - > base + offset ;
while ( 1 ) {
if ( p > = inb - > limit | | p < inb - > base )
die ( " String offset %d overruns string table \n " ,
offset ) ;
if ( * p = = ' \0 ' )
break ;
p + + ;
}
2010-11-18 02:28:20 +03:00
return xstrdup ( inb - > base + offset ) ;
2007-12-18 07:06:42 +03:00
}
static struct property * flat_read_property ( struct inbuf * dtbuf ,
struct inbuf * strbuf , int flags )
{
2008-08-07 06:24:17 +04:00
uint32_t proplen , stroff ;
2007-12-18 07:06:42 +03:00
char * name ;
struct data val ;
proplen = flat_read_word ( dtbuf ) ;
stroff = flat_read_word ( dtbuf ) ;
name = flat_read_stringtable ( strbuf , stroff ) ;
if ( ( flags & FTF_VARALIGN ) & & ( proplen > = 8 ) )
flat_realign ( dtbuf , 8 ) ;
val = flat_read_data ( dtbuf , proplen ) ;
2010-11-18 02:28:20 +03:00
return build_property ( name , val ) ;
2007-12-18 07:06:42 +03:00
}
static struct reserve_info * flat_read_mem_reserve ( struct inbuf * inb )
{
struct reserve_info * reservelist = NULL ;
struct reserve_info * new ;
struct fdt_reserve_entry re ;
/*
* Each entry is a pair of u64 ( addr , size ) values for 4 cell_t ' s .
* List terminates at an entry with size equal to zero .
*
* First pass , count entries .
*/
while ( 1 ) {
flat_read_chunk ( inb , & re , sizeof ( re ) ) ;
2008-08-07 06:24:17 +04:00
re . address = fdt64_to_cpu ( re . address ) ;
re . size = fdt64_to_cpu ( re . size ) ;
2007-12-18 07:06:42 +03:00
if ( re . size = = 0 )
break ;
2010-11-18 02:28:20 +03:00
new = build_reserve_entry ( re . address , re . size ) ;
2007-12-18 07:06:42 +03:00
reservelist = add_reserve_entry ( reservelist , new ) ;
}
return reservelist ;
}
static char * nodename_from_path ( const char * ppath , const char * cpath )
{
int plen ;
2008-08-07 06:24:17 +04:00
plen = strlen ( ppath ) ;
2007-12-18 07:06:42 +03:00
2008-08-07 06:24:17 +04:00
if ( ! strneq ( ppath , cpath , plen ) )
die ( " Path \" %s \" is not valid as a child of \" %s \" \n " ,
cpath , ppath ) ;
2007-12-18 07:06:42 +03:00
2008-08-07 06:24:17 +04:00
/* root node is a special case */
if ( ! streq ( ppath , " / " ) )
plen + + ;
2007-12-18 07:06:42 +03:00
2010-11-18 02:28:20 +03:00
return xstrdup ( cpath + plen ) ;
2007-12-18 07:06:42 +03:00
}
static struct node * unflatten_tree ( struct inbuf * dtbuf ,
struct inbuf * strbuf ,
2008-08-07 06:24:17 +04:00
const char * parent_flatname , int flags )
2007-12-18 07:06:42 +03:00
{
struct node * node ;
2008-08-07 06:24:17 +04:00
char * flatname ;
uint32_t val ;
2007-12-18 07:06:42 +03:00
node = build_node ( NULL , NULL ) ;
2008-08-07 06:24:17 +04:00
flatname = flat_read_string ( dtbuf ) ;
2007-12-18 07:06:42 +03:00
2008-08-07 06:24:17 +04:00
if ( flags & FTF_FULLPATH )
node - > name = nodename_from_path ( parent_flatname , flatname ) ;
else
node - > name = flatname ;
2007-12-18 07:06:42 +03:00
do {
struct property * prop ;
struct node * child ;
val = flat_read_word ( dtbuf ) ;
switch ( val ) {
case FDT_PROP :
if ( node - > children )
fprintf ( stderr , " Warning: Flat tree input has "
" subnodes preceding a property. \n " ) ;
prop = flat_read_property ( dtbuf , strbuf , flags ) ;
add_property ( node , prop ) ;
break ;
case FDT_BEGIN_NODE :
2008-08-07 06:24:17 +04:00
child = unflatten_tree ( dtbuf , strbuf , flatname , flags ) ;
2007-12-18 07:06:42 +03:00
add_child ( node , child ) ;
break ;
case FDT_END_NODE :
break ;
case FDT_END :
die ( " Premature FDT_END in device tree blob \n " ) ;
break ;
case FDT_NOP :
if ( ! ( flags & FTF_NOPS ) )
fprintf ( stderr , " Warning: NOP tag found in flat tree "
" version <16 \n " ) ;
/* Ignore */
break ;
default :
die ( " Invalid opcode word %08x in device tree blob \n " ,
val ) ;
}
} while ( val ! = FDT_END_NODE ) ;
return node ;
}
2008-08-07 06:24:17 +04:00
struct boot_info * dt_from_blob ( const char * fname )
2007-12-18 07:06:42 +03:00
{
2010-11-18 02:28:20 +03:00
FILE * f ;
2008-08-07 06:24:17 +04:00
uint32_t magic , totalsize , version , size_dt , boot_cpuid_phys ;
uint32_t off_dt , off_str , off_mem_rsvmap ;
2007-12-18 07:06:42 +03:00
int rc ;
char * blob ;
struct fdt_header * fdt ;
char * p ;
struct inbuf dtbuf , strbuf ;
struct inbuf memresvbuf ;
int sizeleft ;
struct reserve_info * reservelist ;
struct node * tree ;
2008-08-07 06:24:17 +04:00
uint32_t val ;
2007-12-18 07:06:42 +03:00
int flags = 0 ;
2010-11-18 02:28:20 +03:00
f = srcfile_relative_open ( fname , NULL ) ;
2008-08-07 06:24:17 +04:00
2010-11-18 02:28:20 +03:00
rc = fread ( & magic , sizeof ( magic ) , 1 , f ) ;
if ( ferror ( f ) )
2007-12-18 07:06:42 +03:00
die ( " Error reading DT blob magic number: %s \n " ,
strerror ( errno ) ) ;
if ( rc < 1 ) {
2010-11-18 02:28:20 +03:00
if ( feof ( f ) )
2007-12-18 07:06:42 +03:00
die ( " EOF reading DT blob magic number \n " ) ;
else
die ( " Mysterious short read reading magic number \n " ) ;
}
2008-08-07 06:24:17 +04:00
magic = fdt32_to_cpu ( magic ) ;
2007-12-18 07:06:42 +03:00
if ( magic ! = FDT_MAGIC )
die ( " Blob has incorrect magic number \n " ) ;
2010-11-18 02:28:20 +03:00
rc = fread ( & totalsize , sizeof ( totalsize ) , 1 , f ) ;
if ( ferror ( f ) )
2007-12-18 07:06:42 +03:00
die ( " Error reading DT blob size: %s \n " , strerror ( errno ) ) ;
if ( rc < 1 ) {
2010-11-18 02:28:20 +03:00
if ( feof ( f ) )
2007-12-18 07:06:42 +03:00
die ( " EOF reading DT blob size \n " ) ;
else
die ( " Mysterious short read reading blob size \n " ) ;
}
2008-08-07 06:24:17 +04:00
totalsize = fdt32_to_cpu ( totalsize ) ;
2007-12-18 07:06:42 +03:00
if ( totalsize < FDT_V1_SIZE )
die ( " DT blob size (%d) is too small \n " , totalsize ) ;
blob = xmalloc ( totalsize ) ;
fdt = ( struct fdt_header * ) blob ;
2008-08-07 06:24:17 +04:00
fdt - > magic = cpu_to_fdt32 ( magic ) ;
fdt - > totalsize = cpu_to_fdt32 ( totalsize ) ;
2007-12-18 07:06:42 +03:00
sizeleft = totalsize - sizeof ( magic ) - sizeof ( totalsize ) ;
p = blob + sizeof ( magic ) + sizeof ( totalsize ) ;
while ( sizeleft ) {
2010-11-18 02:28:20 +03:00
if ( feof ( f ) )
2007-12-18 07:06:42 +03:00
die ( " EOF before reading %d bytes of DT blob \n " ,
totalsize ) ;
2010-11-18 02:28:20 +03:00
rc = fread ( p , 1 , sizeleft , f ) ;
if ( ferror ( f ) )
2007-12-18 07:06:42 +03:00
die ( " Error reading DT blob: %s \n " ,
strerror ( errno ) ) ;
sizeleft - = rc ;
p + = rc ;
}
2008-08-07 06:24:17 +04:00
off_dt = fdt32_to_cpu ( fdt - > off_dt_struct ) ;
off_str = fdt32_to_cpu ( fdt - > off_dt_strings ) ;
off_mem_rsvmap = fdt32_to_cpu ( fdt - > off_mem_rsvmap ) ;
version = fdt32_to_cpu ( fdt - > version ) ;
boot_cpuid_phys = fdt32_to_cpu ( fdt - > boot_cpuid_phys ) ;
2007-12-18 07:06:42 +03:00
if ( off_mem_rsvmap > = totalsize )
die ( " Mem Reserve structure offset exceeds total size \n " ) ;
if ( off_dt > = totalsize )
die ( " DT structure offset exceeds total size \n " ) ;
if ( off_str > totalsize )
die ( " String table offset exceeds total size \n " ) ;
if ( version > = 3 ) {
2008-08-07 06:24:17 +04:00
uint32_t size_str = fdt32_to_cpu ( fdt - > size_dt_strings ) ;
2007-12-18 07:06:42 +03:00
if ( off_str + size_str > totalsize )
die ( " String table extends past total size \n " ) ;
2008-08-07 06:24:17 +04:00
inbuf_init ( & strbuf , blob + off_str , blob + off_str + size_str ) ;
} else {
inbuf_init ( & strbuf , blob + off_str , blob + totalsize ) ;
2007-12-18 07:06:42 +03:00
}
if ( version > = 17 ) {
2008-08-07 06:24:17 +04:00
size_dt = fdt32_to_cpu ( fdt - > size_dt_struct ) ;
2007-12-18 07:06:42 +03:00
if ( off_dt + size_dt > totalsize )
die ( " Structure block extends past total size \n " ) ;
}
if ( version < 16 ) {
flags | = FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN ;
} else {
flags | = FTF_NOPS ;
}
inbuf_init ( & memresvbuf ,
blob + off_mem_rsvmap , blob + totalsize ) ;
inbuf_init ( & dtbuf , blob + off_dt , blob + totalsize ) ;
reservelist = flat_read_mem_reserve ( & memresvbuf ) ;
val = flat_read_word ( & dtbuf ) ;
if ( val ! = FDT_BEGIN_NODE )
die ( " Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x) \n " , val ) ;
tree = unflatten_tree ( & dtbuf , & strbuf , " " , flags ) ;
val = flat_read_word ( & dtbuf ) ;
if ( val ! = FDT_END )
die ( " Device tree blob doesn't end with FDT_END \n " ) ;
free ( blob ) ;
2010-11-18 02:28:20 +03:00
fclose ( f ) ;
2008-08-07 06:24:17 +04:00
return build_boot_info ( reservelist , tree , boot_cpuid_phys ) ;
2007-12-18 07:06:42 +03:00
}