2007-12-10 14:28:39 +11:00
/*
* libfdt - Flat Device Tree manipulation
* Copyright ( C ) 2006 David Gibson , IBM Corporation .
*
* libfdt is dual licensed : you can use it either under the terms of
* the GPL , or the BSD license , at your option .
*
* a ) This library 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 library 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 library ; if not , write to the Free
* Software Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston ,
* MA 02110 - 1301 USA
*
* Alternatively ,
*
* b ) Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* 1. Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
* 2. Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR
* OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE ,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "libfdt_env.h"
# include <fdt.h>
# include <libfdt.h>
# include "libfdt_internal.h"
2008-08-07 12:24:17 +10:00
static int _fdt_sw_check_header ( void * fdt )
2007-12-10 14:28:39 +11:00
{
2008-08-07 12:24:17 +10:00
if ( fdt_magic ( fdt ) ! = FDT_SW_MAGIC )
2007-12-10 14:28:39 +11:00
return - FDT_ERR_BADMAGIC ;
2008-08-07 12:24:17 +10:00
/* FIXME: should check more details about the header state */
2007-12-10 14:28:39 +11:00
return 0 ;
}
2008-08-07 12:24:17 +10:00
# define FDT_SW_CHECK_HEADER(fdt) \
{ \
int err ; \
if ( ( err = _fdt_sw_check_header ( fdt ) ) ! = 0 ) \
return err ; \
}
static void * _fdt_grab_space ( void * fdt , int len )
2007-12-10 14:28:39 +11:00
{
int offset = fdt_size_dt_struct ( fdt ) ;
int spaceleft ;
spaceleft = fdt_totalsize ( fdt ) - fdt_off_dt_struct ( fdt )
- fdt_size_dt_strings ( fdt ) ;
if ( ( offset + len < offset ) | | ( offset + len > spaceleft ) )
return NULL ;
fdt_set_size_dt_struct ( fdt , offset + len ) ;
return fdt_offset_ptr_w ( fdt , offset , len ) ;
}
int fdt_create ( void * buf , int bufsize )
{
void * fdt = buf ;
if ( bufsize < sizeof ( struct fdt_header ) )
return - FDT_ERR_NOSPACE ;
memset ( buf , 0 , bufsize ) ;
2008-08-07 12:24:17 +10:00
fdt_set_magic ( fdt , FDT_SW_MAGIC ) ;
2007-12-10 14:28:39 +11:00
fdt_set_version ( fdt , FDT_LAST_SUPPORTED_VERSION ) ;
fdt_set_last_comp_version ( fdt , FDT_FIRST_SUPPORTED_VERSION ) ;
fdt_set_totalsize ( fdt , bufsize ) ;
2008-08-07 12:24:17 +10:00
fdt_set_off_mem_rsvmap ( fdt , FDT_ALIGN ( sizeof ( struct fdt_header ) ,
sizeof ( struct fdt_reserve_entry ) ) ) ;
2007-12-10 14:28:39 +11:00
fdt_set_off_dt_struct ( fdt , fdt_off_mem_rsvmap ( fdt ) ) ;
fdt_set_off_dt_strings ( fdt , bufsize ) ;
return 0 ;
}
int fdt_add_reservemap_entry ( void * fdt , uint64_t addr , uint64_t size )
{
struct fdt_reserve_entry * re ;
int offset ;
2008-08-07 12:24:17 +10:00
FDT_SW_CHECK_HEADER ( fdt ) ;
2007-12-10 14:28:39 +11:00
if ( fdt_size_dt_struct ( fdt ) )
return - FDT_ERR_BADSTATE ;
offset = fdt_off_dt_struct ( fdt ) ;
if ( ( offset + sizeof ( * re ) ) > fdt_totalsize ( fdt ) )
return - FDT_ERR_NOSPACE ;
2008-08-07 12:24:17 +10:00
re = ( struct fdt_reserve_entry * ) ( ( char * ) fdt + offset ) ;
2007-12-10 14:28:39 +11:00
re - > address = cpu_to_fdt64 ( addr ) ;
re - > size = cpu_to_fdt64 ( size ) ;
fdt_set_off_dt_struct ( fdt , offset + sizeof ( * re ) ) ;
return 0 ;
}
int fdt_finish_reservemap ( void * fdt )
{
return fdt_add_reservemap_entry ( fdt , 0 , 0 ) ;
}
int fdt_begin_node ( void * fdt , const char * name )
{
struct fdt_node_header * nh ;
int namelen = strlen ( name ) + 1 ;
2008-08-07 12:24:17 +10:00
FDT_SW_CHECK_HEADER ( fdt ) ;
2007-12-10 14:28:39 +11:00
2008-08-07 12:24:17 +10:00
nh = _fdt_grab_space ( fdt , sizeof ( * nh ) + FDT_TAGALIGN ( namelen ) ) ;
2007-12-10 14:28:39 +11:00
if ( ! nh )
return - FDT_ERR_NOSPACE ;
nh - > tag = cpu_to_fdt32 ( FDT_BEGIN_NODE ) ;
memcpy ( nh - > name , name , namelen ) ;
return 0 ;
}
int fdt_end_node ( void * fdt )
{
uint32_t * en ;
2008-08-07 12:24:17 +10:00
FDT_SW_CHECK_HEADER ( fdt ) ;
2007-12-10 14:28:39 +11:00
2008-08-07 12:24:17 +10:00
en = _fdt_grab_space ( fdt , FDT_TAGSIZE ) ;
2007-12-10 14:28:39 +11:00
if ( ! en )
return - FDT_ERR_NOSPACE ;
* en = cpu_to_fdt32 ( FDT_END_NODE ) ;
return 0 ;
}
2008-08-07 12:24:17 +10:00
static int _fdt_find_add_string ( void * fdt , const char * s )
2007-12-10 14:28:39 +11:00
{
char * strtab = ( char * ) fdt + fdt_totalsize ( fdt ) ;
const char * p ;
int strtabsize = fdt_size_dt_strings ( fdt ) ;
int len = strlen ( s ) + 1 ;
int struct_top , offset ;
p = _fdt_find_string ( strtab - strtabsize , strtabsize , s ) ;
if ( p )
return p - strtab ;
/* Add it */
offset = - strtabsize - len ;
struct_top = fdt_off_dt_struct ( fdt ) + fdt_size_dt_struct ( fdt ) ;
if ( fdt_totalsize ( fdt ) + offset < struct_top )
return 0 ; /* no more room :( */
memcpy ( strtab + offset , s , len ) ;
fdt_set_size_dt_strings ( fdt , strtabsize + len ) ;
return offset ;
}
int fdt_property ( void * fdt , const char * name , const void * val , int len )
{
struct fdt_property * prop ;
int nameoff ;
2008-08-07 12:24:17 +10:00
FDT_SW_CHECK_HEADER ( fdt ) ;
2007-12-10 14:28:39 +11:00
2008-08-07 12:24:17 +10:00
nameoff = _fdt_find_add_string ( fdt , name ) ;
2007-12-10 14:28:39 +11:00
if ( nameoff = = 0 )
return - FDT_ERR_NOSPACE ;
2008-08-07 12:24:17 +10:00
prop = _fdt_grab_space ( fdt , sizeof ( * prop ) + FDT_TAGALIGN ( len ) ) ;
2007-12-10 14:28:39 +11:00
if ( ! prop )
return - FDT_ERR_NOSPACE ;
prop - > tag = cpu_to_fdt32 ( FDT_PROP ) ;
prop - > nameoff = cpu_to_fdt32 ( nameoff ) ;
prop - > len = cpu_to_fdt32 ( len ) ;
memcpy ( prop - > data , val , len ) ;
return 0 ;
}
int fdt_finish ( void * fdt )
{
char * p = ( char * ) fdt ;
uint32_t * end ;
int oldstroffset , newstroffset ;
uint32_t tag ;
int offset , nextoffset ;
2008-08-07 12:24:17 +10:00
FDT_SW_CHECK_HEADER ( fdt ) ;
2007-12-10 14:28:39 +11:00
/* Add terminator */
2008-08-07 12:24:17 +10:00
end = _fdt_grab_space ( fdt , sizeof ( * end ) ) ;
2007-12-10 14:28:39 +11:00
if ( ! end )
return - FDT_ERR_NOSPACE ;
* end = cpu_to_fdt32 ( FDT_END ) ;
/* Relocate the string table */
oldstroffset = fdt_totalsize ( fdt ) - fdt_size_dt_strings ( fdt ) ;
newstroffset = fdt_off_dt_struct ( fdt ) + fdt_size_dt_struct ( fdt ) ;
memmove ( p + newstroffset , p + oldstroffset , fdt_size_dt_strings ( fdt ) ) ;
fdt_set_off_dt_strings ( fdt , newstroffset ) ;
/* Walk the structure, correcting string offsets */
offset = 0 ;
while ( ( tag = fdt_next_tag ( fdt , offset , & nextoffset ) ) ! = FDT_END ) {
if ( tag = = FDT_PROP ) {
struct fdt_property * prop =
fdt_offset_ptr_w ( fdt , offset , sizeof ( * prop ) ) ;
int nameoff ;
if ( ! prop )
return - FDT_ERR_BADSTRUCTURE ;
nameoff = fdt32_to_cpu ( prop - > nameoff ) ;
nameoff + = fdt_size_dt_strings ( fdt ) ;
prop - > nameoff = cpu_to_fdt32 ( nameoff ) ;
}
offset = nextoffset ;
}
/* Finally, adjust the header */
fdt_set_totalsize ( fdt , newstroffset + fdt_size_dt_strings ( fdt ) ) ;
fdt_set_magic ( fdt , FDT_MAGIC ) ;
return 0 ;
}