2019-06-21 08:18:32 -06:00
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2007-12-10 14:28:39 +11:00
/*
* libfdt - Flat Device Tree manipulation
* Copyright ( C ) 2006 David Gibson , IBM Corporation .
*/
# include "libfdt_env.h"
# include <fdt.h>
# include <libfdt.h>
# include "libfdt_internal.h"
2018-09-13 08:59:25 -05:00
static int fdt_sw_probe_ ( void * fdt )
2007-12-10 14:28:39 +11:00
{
2020-03-13 08:56:58 -05:00
if ( ! can_assume ( VALID_INPUT ) ) {
if ( fdt_magic ( fdt ) = = FDT_MAGIC )
return - FDT_ERR_BADSTATE ;
else if ( fdt_magic ( fdt ) ! = FDT_SW_MAGIC )
return - FDT_ERR_BADMAGIC ;
}
2007-12-10 14:28:39 +11:00
return 0 ;
}
2018-09-13 08:59:25 -05:00
# define FDT_SW_PROBE(fdt) \
{ \
int err ; \
if ( ( err = fdt_sw_probe_ ( fdt ) ) ! = 0 ) \
return err ; \
}
/* 'memrsv' state: Initial state after fdt_create()
*
* Allowed functions :
2020-06-29 12:15:13 -06:00
* fdt_add_reservemap_entry ( )
2018-09-13 08:59:25 -05:00
* fdt_finish_reservemap ( ) [ moves to ' struct ' state ]
*/
static int fdt_sw_probe_memrsv_ ( void * fdt )
{
int err = fdt_sw_probe_ ( fdt ) ;
if ( err )
return err ;
2020-03-13 08:56:58 -05:00
if ( ! can_assume ( VALID_INPUT ) & & fdt_off_dt_strings ( fdt ) ! = 0 )
2018-09-13 08:59:25 -05:00
return - FDT_ERR_BADSTATE ;
return 0 ;
}
# define FDT_SW_PROBE_MEMRSV(fdt) \
{ \
int err ; \
if ( ( err = fdt_sw_probe_memrsv_ ( fdt ) ) ! = 0 ) \
return err ; \
}
/* 'struct' state: Enter this state after fdt_finish_reservemap()
*
* Allowed functions :
* fdt_begin_node ( )
* fdt_end_node ( )
* fdt_property * ( )
* fdt_finish ( ) [ moves to ' complete ' state ]
*/
static int fdt_sw_probe_struct_ ( void * fdt )
{
int err = fdt_sw_probe_ ( fdt ) ;
if ( err )
return err ;
2020-03-13 08:56:58 -05:00
if ( ! can_assume ( VALID_INPUT ) & &
fdt_off_dt_strings ( fdt ) ! = fdt_totalsize ( fdt ) )
2018-09-13 08:59:25 -05:00
return - FDT_ERR_BADSTATE ;
return 0 ;
}
# define FDT_SW_PROBE_STRUCT(fdt) \
2008-08-07 12:24:17 +10:00
{ \
int err ; \
2018-09-13 08:59:25 -05:00
if ( ( err = fdt_sw_probe_struct_ ( fdt ) ) ! = 0 ) \
2008-08-07 12:24:17 +10:00
return err ; \
}
2019-06-12 07:05:52 -06:00
static inline uint32_t sw_flags ( void * fdt )
{
/* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
return fdt_last_comp_version ( fdt ) ;
}
2018-09-13 08:59:25 -05:00
/* 'complete' state: Enter this state after fdt_finish()
*
* Allowed functions : none
*/
2018-02-27 17:40:38 -06:00
static void * fdt_grab_space_ ( void * fdt , size_t len )
2007-12-10 14:28:39 +11:00
{
2020-10-12 09:58:15 -05:00
unsigned int offset = fdt_size_dt_struct ( fdt ) ;
unsigned int spaceleft ;
2007-12-10 14:28:39 +11:00
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 ) ;
2018-02-27 17:40:38 -06:00
return fdt_offset_ptr_w_ ( fdt , offset ) ;
2007-12-10 14:28:39 +11:00
}
2019-06-12 07:05:52 -06:00
int fdt_create_with_flags ( void * buf , int bufsize , uint32_t flags )
2007-12-10 14:28:39 +11:00
{
2020-10-12 09:58:15 -05:00
const int hdrsize = FDT_ALIGN ( sizeof ( struct fdt_header ) ,
sizeof ( struct fdt_reserve_entry ) ) ;
2007-12-10 14:28:39 +11:00
void * fdt = buf ;
2018-09-13 08:59:25 -05:00
if ( bufsize < hdrsize )
2007-12-10 14:28:39 +11:00
return - FDT_ERR_NOSPACE ;
2019-06-12 07:05:52 -06:00
if ( flags & ~ FDT_CREATE_FLAGS_ALL )
return - FDT_ERR_BADFLAGS ;
2007-12-10 14:28:39 +11:00
memset ( buf , 0 , bufsize ) ;
2019-06-12 07:05:52 -06:00
/*
* magic and last_comp_version keep intermediate state during the fdt
* creation process , which is replaced with the proper FDT format by
* fdt_finish ( ) .
*
* flags should be accessed with sw_flags ( ) .
*/
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 ) ;
2019-06-12 07:05:52 -06:00
fdt_set_last_comp_version ( fdt , flags ) ;
2007-12-10 14:28:39 +11:00
fdt_set_totalsize ( fdt , bufsize ) ;
2018-09-13 08:59:25 -05:00
fdt_set_off_mem_rsvmap ( fdt , hdrsize ) ;
2007-12-10 14:28:39 +11:00
fdt_set_off_dt_struct ( fdt , fdt_off_mem_rsvmap ( fdt ) ) ;
2018-09-13 08:59:25 -05:00
fdt_set_off_dt_strings ( fdt , 0 ) ;
2007-12-10 14:28:39 +11:00
return 0 ;
}
2019-06-12 07:05:52 -06:00
int fdt_create ( void * buf , int bufsize )
{
return fdt_create_with_flags ( buf , bufsize , 0 ) ;
}
2015-04-29 16:00:05 -05:00
int fdt_resize ( void * fdt , void * buf , int bufsize )
{
size_t headsize , tailsize ;
char * oldtail , * newtail ;
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE ( fdt ) ;
2015-04-29 16:00:05 -05:00
2020-10-12 09:58:15 -05:00
if ( bufsize < 0 )
return - FDT_ERR_NOSPACE ;
2018-09-13 08:59:25 -05:00
headsize = fdt_off_dt_struct ( fdt ) + fdt_size_dt_struct ( fdt ) ;
2015-04-29 16:00:05 -05:00
tailsize = fdt_size_dt_strings ( fdt ) ;
2020-03-13 08:56:58 -05:00
if ( ! can_assume ( VALID_DTB ) & &
headsize + tailsize > fdt_totalsize ( fdt ) )
2018-09-13 08:59:25 -05:00
return - FDT_ERR_INTERNAL ;
2020-10-12 09:58:15 -05:00
if ( ( headsize + tailsize ) > ( unsigned ) bufsize )
2015-04-29 16:00:05 -05:00
return - FDT_ERR_NOSPACE ;
oldtail = ( char * ) fdt + fdt_totalsize ( fdt ) - tailsize ;
newtail = ( char * ) buf + bufsize - tailsize ;
/* Two cases to avoid clobbering data if the old and new
* buffers partially overlap */
if ( buf < = fdt ) {
memmove ( buf , fdt , headsize ) ;
memmove ( newtail , oldtail , tailsize ) ;
} else {
memmove ( newtail , oldtail , tailsize ) ;
memmove ( buf , fdt , headsize ) ;
}
fdt_set_totalsize ( buf , bufsize ) ;
2018-09-13 08:59:25 -05:00
if ( fdt_off_dt_strings ( buf ) )
fdt_set_off_dt_strings ( buf , bufsize ) ;
2015-04-29 16:00:05 -05:00
return 0 ;
}
2007-12-10 14:28:39 +11:00
int fdt_add_reservemap_entry ( void * fdt , uint64_t addr , uint64_t size )
{
struct fdt_reserve_entry * re ;
int offset ;
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE_MEMRSV ( fdt ) ;
2007-12-10 14:28:39 +11:00
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 )
{
2018-09-13 08:59:25 -05:00
int err = fdt_add_reservemap_entry ( fdt , 0 , 0 ) ;
if ( err )
return err ;
fdt_set_off_dt_strings ( fdt , fdt_totalsize ( fdt ) ) ;
return 0 ;
2007-12-10 14:28:39 +11:00
}
int fdt_begin_node ( void * fdt , const char * name )
{
struct fdt_node_header * nh ;
2018-09-13 08:59:25 -05:00
int namelen ;
2007-12-10 14:28:39 +11:00
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE_STRUCT ( fdt ) ;
2007-12-10 14:28:39 +11:00
2018-09-13 08:59:25 -05:00
namelen = strlen ( name ) + 1 ;
2018-02-27 17:40:38 -06: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 )
{
2015-04-29 16:00:05 -05:00
fdt32_t * en ;
2007-12-10 14:28:39 +11:00
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE_STRUCT ( fdt ) ;
2007-12-10 14:28:39 +11:00
2018-02-27 17:40:38 -06: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 ;
}
2019-06-12 07:05:52 -06:00
static int fdt_add_string_ ( void * fdt , const char * s )
2007-12-10 14:28:39 +11:00
{
char * strtab = ( char * ) fdt + fdt_totalsize ( fdt ) ;
2020-10-12 09:58:15 -05:00
unsigned int strtabsize = fdt_size_dt_strings ( fdt ) ;
unsigned int len = strlen ( s ) + 1 ;
unsigned int struct_top , offset ;
2007-12-10 14:28:39 +11:00
2020-10-12 09:58:15 -05:00
offset = strtabsize + len ;
2007-12-10 14:28:39 +11:00
struct_top = fdt_off_dt_struct ( fdt ) + fdt_size_dt_struct ( fdt ) ;
2020-10-12 09:58:15 -05:00
if ( fdt_totalsize ( fdt ) - offset < struct_top )
2007-12-10 14:28:39 +11:00
return 0 ; /* no more room :( */
2020-10-12 09:58:15 -05:00
memcpy ( strtab - offset , s , len ) ;
2007-12-10 14:28:39 +11:00
fdt_set_size_dt_strings ( fdt , strtabsize + len ) ;
2020-10-12 09:58:15 -05:00
return - offset ;
2007-12-10 14:28:39 +11:00
}
2019-06-12 07:05:52 -06:00
/* Must only be used to roll back in case of error */
static void fdt_del_last_string_ ( void * fdt , const char * s )
{
int strtabsize = fdt_size_dt_strings ( fdt ) ;
int len = strlen ( s ) + 1 ;
fdt_set_size_dt_strings ( fdt , strtabsize - len ) ;
}
static int fdt_find_add_string_ ( void * fdt , const char * s , int * allocated )
{
char * strtab = ( char * ) fdt + fdt_totalsize ( fdt ) ;
int strtabsize = fdt_size_dt_strings ( fdt ) ;
const char * p ;
* allocated = 0 ;
p = fdt_find_string_ ( strtab - strtabsize , strtabsize , s ) ;
if ( p )
return p - strtab ;
* allocated = 1 ;
return fdt_add_string_ ( fdt , s ) ;
}
2017-10-03 11:37:04 -05:00
int fdt_property_placeholder ( void * fdt , const char * name , int len , void * * valp )
2007-12-10 14:28:39 +11:00
{
struct fdt_property * prop ;
int nameoff ;
2019-06-12 07:05:52 -06:00
int allocated ;
2007-12-10 14:28:39 +11:00
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE_STRUCT ( fdt ) ;
2007-12-10 14:28:39 +11:00
2019-06-12 07:05:52 -06:00
/* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
if ( sw_flags ( fdt ) & FDT_CREATE_FLAG_NO_NAME_DEDUP ) {
allocated = 1 ;
nameoff = fdt_add_string_ ( fdt , name ) ;
} else {
nameoff = fdt_find_add_string_ ( fdt , name , & allocated ) ;
}
2007-12-10 14:28:39 +11:00
if ( nameoff = = 0 )
return - FDT_ERR_NOSPACE ;
2018-02-27 17:40:38 -06:00
prop = fdt_grab_space_ ( fdt , sizeof ( * prop ) + FDT_TAGALIGN ( len ) ) ;
2019-06-12 07:05:52 -06:00
if ( ! prop ) {
if ( allocated )
fdt_del_last_string_ ( fdt , name ) ;
2007-12-10 14:28:39 +11:00
return - FDT_ERR_NOSPACE ;
2019-06-12 07:05:52 -06:00
}
2007-12-10 14:28:39 +11:00
prop - > tag = cpu_to_fdt32 ( FDT_PROP ) ;
prop - > nameoff = cpu_to_fdt32 ( nameoff ) ;
prop - > len = cpu_to_fdt32 ( len ) ;
2017-10-03 11:37:04 -05:00
* valp = prop - > data ;
return 0 ;
}
int fdt_property ( void * fdt , const char * name , const void * val , int len )
{
void * ptr ;
int ret ;
ret = fdt_property_placeholder ( fdt , name , len , & ptr ) ;
if ( ret )
return ret ;
memcpy ( ptr , val , len ) ;
2007-12-10 14:28:39 +11:00
return 0 ;
}
int fdt_finish ( void * fdt )
{
char * p = ( char * ) fdt ;
2015-04-29 16:00:05 -05:00
fdt32_t * end ;
2007-12-10 14:28:39 +11:00
int oldstroffset , newstroffset ;
uint32_t tag ;
int offset , nextoffset ;
2018-09-13 08:59:25 -05:00
FDT_SW_PROBE_STRUCT ( fdt ) ;
2007-12-10 14:28:39 +11:00
/* Add terminator */
2018-02-27 17:40:38 -06: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 =
2018-02-27 17:40:38 -06:00
fdt_offset_ptr_w_ ( fdt , offset ) ;
2007-12-10 14:28:39 +11:00
int nameoff ;
nameoff = fdt32_to_cpu ( prop - > nameoff ) ;
nameoff + = fdt_size_dt_strings ( fdt ) ;
prop - > nameoff = cpu_to_fdt32 ( nameoff ) ;
}
offset = nextoffset ;
}
2012-09-28 21:25:59 +00:00
if ( nextoffset < 0 )
return nextoffset ;
2007-12-10 14:28:39 +11:00
/* Finally, adjust the header */
fdt_set_totalsize ( fdt , newstroffset + fdt_size_dt_strings ( fdt ) ) ;
2019-06-12 07:05:52 -06:00
/* And fix up fields that were keeping intermediate state. */
2021-02-03 15:26:03 -06:00
fdt_set_last_comp_version ( fdt , FDT_LAST_COMPATIBLE_VERSION ) ;
2007-12-10 14:28:39 +11:00
fdt_set_magic ( fdt , FDT_MAGIC ) ;
2019-06-12 07:05:52 -06:00
2007-12-10 14:28:39 +11:00
return 0 ;
}