iPXE
Data Structures | Functions | Variables
fdt.c File Reference

Flattened Device Tree. More...

#include <string.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/fdt.h>

Go to the source code of this file.

Data Structures

struct  fdt_cursor
 A position within a device tree. More...
struct  fdt_descriptor
 A lexical descriptor. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int fdt_exists (void)
 Check if device tree exists.
static int fdt_traverse (struct fdt_cursor *pos, struct fdt_descriptor *desc)
 Traverse device tree.
static int fdt_child (unsigned int offset, const char *name, unsigned int *child)
 Find child node.
int fdt_path (const char *path, unsigned int *offset)
 Find node by path.
int fdt_alias (const char *name, unsigned int *offset)
 Find node by alias.
static int fdt_property (unsigned int offset, const char *name, struct fdt_descriptor *desc)
 Find property.
const char * fdt_string (unsigned int offset, const char *name)
 Find string property.
int fdt_mac (unsigned int offset, struct net_device *netdev)
 Get MAC address from property.
int register_fdt (const struct fdt_header *hdr)
 Register device tree.
 REQUIRING_SYMBOL (register_fdt)
 REQUIRE_OBJECT (config_fdt)

Variables

static struct fdt fdt
 The system flattened device tree (if present)

Detailed Description

Flattened Device Tree.

Definition in file fdt.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int fdt_exists ( void  ) [inline, static]

Check if device tree exists.

Parameters:
has_fdtDevice tree exists

Definition at line 65 of file fdt.c.

References fdt::hdr, and NULL.

                                                                        {

        return ( fdt.hdr != NULL );
}
static int fdt_traverse ( struct fdt_cursor pos,
struct fdt_descriptor desc 
) [static]

Traverse device tree.

Parameters:
posPosition within device tree
descLexical descriptor to fill in
Return values:
rcReturn status code

Definition at line 77 of file fdt.c.

References assert, be32_to_cpu, cpu_to_be32, fdt_descriptor::data, data, DBGC, fdt_cursor::depth, EINVAL, ENOENT, FDT_BEGIN_NODE, FDT_END_NODE, FDT_NOP, FDT_PROP, FDT_STRUCTURE_ALIGN, fdt_descriptor::len, fdt_prop::len, fdt::len, len, memset(), fdt_descriptor::name, fdt_prop::name_off, name_off, fdt_cursor::offset, fdt::raw, fdt::strings, fdt::strings_len, strnlen(), fdt::structure, and token.

Referenced by fdt_child(), and fdt_property().

                                                        {
        const fdt_token_t *token;
        const void *data;
        const struct fdt_prop *prop;
        unsigned int name_off;
        size_t remaining;
        size_t len;

        /* Sanity checks */
        assert ( pos->offset < fdt.len );
        assert ( ( pos->offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 );

        /* Clear descriptor */
        memset ( desc, 0, sizeof ( *desc ) );

        /* Locate token and calculate remaining space */
        token = ( fdt.raw + fdt.structure + pos->offset );
        remaining = ( fdt.len - pos->offset );
        if ( remaining < sizeof ( *token ) ) {
                DBGC ( &fdt, "FDT truncated tree at +%#04x\n", pos->offset );
                return -EINVAL;
        }
        remaining -= sizeof ( *token );
        data = ( ( ( const void * ) token ) + sizeof ( *token ) );
        len = 0;

        /* Handle token */
        switch ( *token ) {

        case cpu_to_be32 ( FDT_BEGIN_NODE ):

                /* Start of node */
                desc->name = data;
                len = ( strnlen ( desc->name, remaining ) + 1 /* NUL */ );
                if ( remaining < len ) {
                        DBGC ( &fdt, "FDT unterminated node name at +%#04x\n",
                               pos->offset );
                        return -EINVAL;
                }
                pos->depth++;
                break;

        case cpu_to_be32 ( FDT_END_NODE ):

                /* End of node */
                if ( pos->depth < 0 ) {
                        DBGC ( &fdt, "FDT spurious node end at +%#04x\n",
                               pos->offset );
                        return -EINVAL;
                }
                pos->depth--;
                if ( pos->depth < 0 ) {
                        /* End of (sub)tree */
                        return -ENOENT;
                }
                break;

        case cpu_to_be32 ( FDT_PROP ):

                /* Property */
                prop = data;
                if ( remaining < sizeof ( *prop ) ) {
                        DBGC ( &fdt, "FDT truncated property at +%#04x\n",
                               pos->offset );
                        return -EINVAL;
                }
                desc->data = ( ( ( const void * ) prop ) + sizeof ( *prop ) );
                desc->len = be32_to_cpu ( prop->len );
                len = ( sizeof ( *prop ) + desc->len );
                if ( remaining < len ) {
                        DBGC ( &fdt, "FDT overlength property at +%#04x\n",
                               pos->offset );
                        return -EINVAL;
                }
                name_off = be32_to_cpu ( prop->name_off );
                if ( name_off > fdt.strings_len ) {
                        DBGC ( &fdt, "FDT property name outside strings "
                               "block at +%#04x\n", pos->offset );
                        return -EINVAL;
                }
                desc->name = ( fdt.raw + fdt.strings + name_off );
                break;

        case cpu_to_be32 ( FDT_NOP ):

                /* Do nothing */
                break;

        default:

                /* Unrecognised or unexpected token */
                DBGC ( &fdt, "FDT unexpected token %#08x at +%#04x\n",
                       be32_to_cpu ( *token ), pos->offset );
                return -EINVAL;
        }

        /* Update cursor */
        len = ( ( len + FDT_STRUCTURE_ALIGN - 1 ) &
                ~( FDT_STRUCTURE_ALIGN - 1 ) );
        pos->offset += ( sizeof ( *token ) + len );

        /* Sanity checks */
        assert ( pos->offset <= fdt.len );

        return 0;
}
static int fdt_child ( unsigned int  offset,
const char *  name,
unsigned int *  child 
) [static]

Find child node.

Parameters:
offsetStarting node offset
nameNode name
childChild node offset to fill in
Return values:
rcReturn status code

Definition at line 193 of file fdt.c.

References fdt_descriptor::data, DBGC, DBGC2, fdt_cursor::depth, fdt_traverse(), fdt_descriptor::name, fdt_cursor::offset, offset, rc, strcmp(), and strerror().

Referenced by fdt_alias(), and fdt_path().

                                             {
        struct fdt_cursor pos;
        struct fdt_descriptor desc;
        unsigned int orig_offset;
        int rc;

        /* Record original offset (for debugging) */
        orig_offset = offset;

        /* Initialise cursor */
        pos.offset = offset;
        pos.depth = -1;

        /* Find child node */
        while ( 1 ) {

                /* Record current offset */
                *child = pos.offset;

                /* Traverse tree */
                if ( ( rc = fdt_traverse ( &pos, &desc ) ) != 0 ) {
                        DBGC ( &fdt, "FDT +%#04x has no child node \"%s\": "
                               "%s\n", orig_offset, name, strerror ( rc ) );
                        return rc;
                }

                /* Check for matching immediate child node */
                if ( ( pos.depth == 1 ) && desc.name && ( ! desc.data ) ) {
                        DBGC2 ( &fdt, "FDT +%#04x has child node \"%s\"\n",
                                orig_offset, desc.name );
                        if ( strcmp ( name, desc.name ) == 0 ) {
                                DBGC2 ( &fdt, "FDT +%#04x found child node "
                                        "\"%s\" at +%#04x\n", orig_offset,
                                        desc.name, *child );
                                return 0;
                        }
                }
        }
}
int fdt_path ( const char *  path,
unsigned int *  offset 
)

Find node by path.

Parameters:
pathNode path
offsetOffset to fill in
Return values:
rcReturn status code

Definition at line 241 of file fdt.c.

References DBGC2, fdt_child(), rc, and strchr().

Referenced by fdt_alias().

                                                        {
        char *tmp = ( ( char * ) path );
        char *del;
        int rc;

        /* Initialise offset */
        *offset = 0;

        /* Traverse tree one path segment at a time */
        while ( *tmp ) {

                /* Skip any leading '/' */
                while ( *tmp == '/' )
                        tmp++;

                /* Find next '/' delimiter and convert to NUL */
                del = strchr ( tmp, '/' );
                if ( del )
                        *del = '\0';

                /* Find child and restore delimiter */
                rc = fdt_child ( *offset, tmp, offset );
                if ( del )
                        *del = '/';
                if ( rc != 0 )
                        return rc;

                /* Move to next path component, if any */
                while ( *tmp && ( *tmp != '/' ) )
                        tmp++;
        }

        DBGC2 ( &fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset );
        return 0;
}
int fdt_alias ( const char *  name,
unsigned int *  offset 
)

Find node by alias.

Parameters:
nameAlias name
offsetOffset to fill in
Return values:
rcReturn status code

Definition at line 284 of file fdt.c.

References DBGC, ENOENT, fdt_child(), fdt_path(), fdt_string(), NULL, and rc.

Referenced by smscusb_fdt_fetch_mac().

                                                         {
        const char *alias;
        int rc;

        /* Locate "/aliases" node */
        if ( ( rc = fdt_child ( 0, "aliases", offset ) ) != 0 )
                return rc;

        /* Locate alias property */
        if ( ( alias = fdt_string ( *offset, name ) ) == NULL )
                return -ENOENT;
        DBGC ( &fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias );

        /* Locate aliased node */
        if ( ( rc = fdt_path ( alias, offset ) ) != 0 )
                return rc;

        return 0;
}
static int fdt_property ( unsigned int  offset,
const char *  name,
struct fdt_descriptor desc 
) [static]

Find property.

Parameters:
offsetStarting node offset
nameProperty name
descLexical descriptor to fill in
Return values:
rcReturn status code

Definition at line 312 of file fdt.c.

References fdt_descriptor::data, DBGC, DBGC2, DBGC2_HDA, fdt_cursor::depth, fdt_traverse(), fdt_descriptor::len, fdt_descriptor::name, fdt_cursor::offset, offset, rc, strcmp(), and strerror().

Referenced by fdt_mac(), and fdt_string().

                                                        {
        struct fdt_cursor pos;
        int rc;

        /* Initialise cursor */
        pos.offset = offset;
        pos.depth = -1;

        /* Find property */
        while ( 1 ) {

                /* Traverse tree */
                if ( ( rc = fdt_traverse ( &pos, desc ) ) != 0 ) {
                        DBGC ( &fdt, "FDT +%#04x has no property \"%s\": %s\n",
                               offset, name, strerror ( rc ) );
                        return rc;
                }

                /* Check for matching immediate child property */
                if ( ( pos.depth == 0 ) && desc->data ) {
                        DBGC2 ( &fdt, "FDT +%#04x has property \"%s\" len "
                                "%#zx\n", offset, desc->name, desc->len );
                        if ( strcmp ( name, desc->name ) == 0 ) {
                                DBGC2 ( &fdt, "FDT +%#04x found property "
                                        "\"%s\"\n", offset, desc->name );
                                DBGC2_HDA ( &fdt, 0, desc->data, desc->len );
                                return 0;
                        }
                }
        }
}
const char* fdt_string ( unsigned int  offset,
const char *  name 
)

Find string property.

Parameters:
offsetStarting node offset
nameProperty name
Return values:
stringString property, or NULL on error

Definition at line 352 of file fdt.c.

References fdt_descriptor::data, DBGC, fdt_property(), fdt_descriptor::len, NULL, rc, and strnlen().

Referenced by fdt_alias(), and register_fdt().

                                                                  {
        struct fdt_descriptor desc;
        int rc;

        /* Find property */
        if ( ( rc = fdt_property ( offset, name, &desc ) ) != 0 )
                return NULL;

        /* Check NUL termination */
        if ( strnlen ( desc.data, desc.len ) == desc.len ) {
                DBGC ( &fdt, "FDT unterminated string property \"%s\"\n",
                       name );
                return NULL;
        }

        return desc.data;
}
int fdt_mac ( unsigned int  offset,
struct net_device netdev 
)

Get MAC address from property.

Parameters:
offsetStarting node offset
netdevNetwork device
Return values:
rcReturn status code

Definition at line 377 of file fdt.c.

References fdt_descriptor::data, DBGC, DBGC_HDA, ERANGE, fdt_property(), net_device::hw_addr, ll_protocol::hw_addr_len, fdt_descriptor::len, len, net_device::ll_protocol, memcpy(), fdt_descriptor::name, and rc.

Referenced by smscusb_fdt_fetch_mac().

                                                               {
        struct fdt_descriptor desc;
        size_t len;
        int rc;

        /* Find applicable MAC address property */
        if ( ( ( rc = fdt_property ( offset, "mac-address", &desc ) ) != 0 ) &&
             ( ( rc = fdt_property ( offset, "local-mac-address",
                                     &desc ) ) != 0 ) ) {
                return rc;
        }

        /* Check length */
        len = netdev->ll_protocol->hw_addr_len;
        if ( len != desc.len ) {
                DBGC ( &fdt, "FDT malformed MAC address \"%s\":\n",
                       desc.name );
                DBGC_HDA ( &fdt, 0, desc.data, desc.len );
                return -ERANGE;
        }

        /* Fill in MAC address */
        memcpy ( netdev->hw_addr, desc.data, len );

        return 0;
}
int register_fdt ( const struct fdt_header hdr)

Register device tree.

Parameters:
fdtDevice tree header
Return values:
rcReturn status code

Definition at line 410 of file fdt.c.

References be32_to_cpu, cpu_to_be32, DBGC, DBGC_HDA, EINVAL, end, FDT_MAGIC, fdt_string(), FDT_STRUCTURE_ALIGN, FDT_VERSION, hdr, fdt::hdr, fdt_header::last_comp_version, fdt::len, fdt_header::magic, NULL, fdt_header::off_dt_strings, fdt_header::off_dt_struct, fdt::raw, fdt_header::size_dt_strings, fdt_header::size_dt_struct, fdt::strings, fdt::strings_len, fdt::structure, fdt::structure_len, fdt_header::totalsize, and fdt_header::version.

Referenced by efi_fdt_init().

                                                  {
        const uint8_t *end;

        /* Record device tree location */
        fdt.hdr = hdr;
        fdt.len = be32_to_cpu ( hdr->totalsize );
        DBGC ( &fdt, "FDT version %d at %p+%#04zx\n",
               be32_to_cpu ( hdr->version ), fdt.hdr, fdt.len );

        /* Check signature */
        if ( hdr->magic != cpu_to_be32 ( FDT_MAGIC ) ) {
                DBGC ( &fdt, "FDT has invalid magic value %#08x\n",
                       be32_to_cpu ( hdr->magic ) );
                goto err;
        }

        /* Check version */
        if ( hdr->last_comp_version != cpu_to_be32 ( FDT_VERSION ) ) {
                DBGC ( &fdt, "FDT unsupported version %d\n",
                       be32_to_cpu ( hdr->last_comp_version ) );
                goto err;
        }

        /* Record structure block location */
        fdt.structure = be32_to_cpu ( hdr->off_dt_struct );
        fdt.structure_len = be32_to_cpu ( hdr->size_dt_struct );
        DBGC ( &fdt, "FDT structure block at +[%#04x,%#04zx)\n",
               fdt.structure, ( fdt.structure + fdt.structure_len ) );
        if ( ( fdt.structure > fdt.len ) ||
             ( fdt.structure_len > ( fdt.len - fdt.structure ) ) ) {
                DBGC ( &fdt, "FDT structure block exceeds table\n" );
                goto err;
        }
        if ( ( fdt.structure | fdt.structure_len ) &
             ( FDT_STRUCTURE_ALIGN - 1 ) ) {
                DBGC ( &fdt, "FDT structure block is misaligned\n" );
                goto err;
        }

        /* Record strings block location */
        fdt.strings = be32_to_cpu ( hdr->off_dt_strings );
        fdt.strings_len = be32_to_cpu ( hdr->size_dt_strings );
        DBGC ( &fdt, "FDT strings block at +[%#04x,%#04zx)\n",
               fdt.strings, ( fdt.strings + fdt.strings_len ) );
        if ( ( fdt.strings > fdt.len ) ||
             ( fdt.strings_len > ( fdt.len - fdt.strings ) ) ) {
                DBGC ( &fdt, "FDT strings block exceeds table\n" );
                goto err;
        }

        /* Shrink strings block to ensure NUL termination safety */
        end = ( fdt.raw + fdt.strings + fdt.strings_len );
        for ( ; fdt.strings_len ; fdt.strings_len-- ) {
                if ( *(--end) == '\0' )
                        break;
        }
        if ( fdt.strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) {
                DBGC ( &fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n",
                       fdt.strings, ( fdt.strings + fdt.strings_len ) );
        }

        /* Print model name (for debugging) */
        DBGC ( &fdt, "FDT model is \"%s\"\n", fdt_string ( 0, "model" ) );

        return 0;

 err:
        DBGC_HDA ( &fdt, 0, hdr, sizeof ( *hdr ) );
        fdt.hdr = NULL;
        return -EINVAL;
}
REQUIRE_OBJECT ( config_fdt  )

Variable Documentation

struct fdt fdt [static]

The system flattened device tree (if present)

Definition at line 40 of file fdt.c.