Browse Source
Change-Id: Iad7adaf0b16a3d086594cb3432210ac2c4e207f8 Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>pull/1643/head
Antonio Nino Diaz
6 years ago
13 changed files with 5753 additions and 0 deletions
@ -0,0 +1,111 @@ |
|||
#ifndef FDT_H |
|||
#define FDT_H |
|||
/*
|
|||
* libfdt - Flat Device Tree manipulation |
|||
* Copyright (C) 2006 David Gibson, IBM Corporation. |
|||
* Copyright 2012 Kim Phillips, Freescale Semiconductor. |
|||
* |
|||
* 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. |
|||
*/ |
|||
|
|||
#ifndef __ASSEMBLY__ |
|||
|
|||
struct fdt_header { |
|||
fdt32_t magic; /* magic word FDT_MAGIC */ |
|||
fdt32_t totalsize; /* total size of DT block */ |
|||
fdt32_t off_dt_struct; /* offset to structure */ |
|||
fdt32_t off_dt_strings; /* offset to strings */ |
|||
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ |
|||
fdt32_t version; /* format version */ |
|||
fdt32_t last_comp_version; /* last compatible version */ |
|||
|
|||
/* version 2 fields below */ |
|||
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
|
|||
booting on */ |
|||
/* version 3 fields below */ |
|||
fdt32_t size_dt_strings; /* size of the strings block */ |
|||
|
|||
/* version 17 fields below */ |
|||
fdt32_t size_dt_struct; /* size of the structure block */ |
|||
}; |
|||
|
|||
struct fdt_reserve_entry { |
|||
fdt64_t address; |
|||
fdt64_t size; |
|||
}; |
|||
|
|||
struct fdt_node_header { |
|||
fdt32_t tag; |
|||
char name[0]; |
|||
}; |
|||
|
|||
struct fdt_property { |
|||
fdt32_t tag; |
|||
fdt32_t len; |
|||
fdt32_t nameoff; |
|||
char data[0]; |
|||
}; |
|||
|
|||
#endif /* !__ASSEMBLY */ |
|||
|
|||
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ |
|||
#define FDT_TAGSIZE sizeof(fdt32_t) |
|||
|
|||
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ |
|||
#define FDT_END_NODE 0x2 /* End node */ |
|||
#define FDT_PROP 0x3 /* Property: name off, |
|||
size, content */ |
|||
#define FDT_NOP 0x4 /* nop */ |
|||
#define FDT_END 0x9 |
|||
|
|||
#define FDT_V1_SIZE (7*sizeof(fdt32_t)) |
|||
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) |
|||
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) |
|||
#define FDT_V16_SIZE FDT_V3_SIZE |
|||
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) |
|||
|
|||
#endif /* FDT_H */ |
File diff suppressed because it is too large
@ -0,0 +1,140 @@ |
|||
#ifndef LIBFDT_ENV_H |
|||
#define LIBFDT_ENV_H |
|||
/*
|
|||
* libfdt - Flat Device Tree manipulation |
|||
* Copyright (C) 2006 David Gibson, IBM Corporation. |
|||
* Copyright 2012 Kim Phillips, Freescale Semiconductor. |
|||
* |
|||
* 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 <stddef.h> |
|||
#include <stdint.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <limits.h> |
|||
|
|||
#ifdef __CHECKER__ |
|||
#define FDT_FORCE __attribute__((force)) |
|||
#define FDT_BITWISE __attribute__((bitwise)) |
|||
#else |
|||
#define FDT_FORCE |
|||
#define FDT_BITWISE |
|||
#endif |
|||
|
|||
typedef uint16_t FDT_BITWISE fdt16_t; |
|||
typedef uint32_t FDT_BITWISE fdt32_t; |
|||
typedef uint64_t FDT_BITWISE fdt64_t; |
|||
|
|||
#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) |
|||
#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) |
|||
#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ |
|||
(EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) |
|||
#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ |
|||
(EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ |
|||
(EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ |
|||
(EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) |
|||
|
|||
static inline uint16_t fdt16_to_cpu(fdt16_t x) |
|||
{ |
|||
return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); |
|||
} |
|||
static inline fdt16_t cpu_to_fdt16(uint16_t x) |
|||
{ |
|||
return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); |
|||
} |
|||
|
|||
static inline uint32_t fdt32_to_cpu(fdt32_t x) |
|||
{ |
|||
return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); |
|||
} |
|||
static inline fdt32_t cpu_to_fdt32(uint32_t x) |
|||
{ |
|||
return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); |
|||
} |
|||
|
|||
static inline uint64_t fdt64_to_cpu(fdt64_t x) |
|||
{ |
|||
return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); |
|||
} |
|||
static inline fdt64_t cpu_to_fdt64(uint64_t x) |
|||
{ |
|||
return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); |
|||
} |
|||
#undef CPU_TO_FDT64 |
|||
#undef CPU_TO_FDT32 |
|||
#undef CPU_TO_FDT16 |
|||
#undef EXTRACT_BYTE |
|||
|
|||
#ifdef __APPLE__ |
|||
#include <AvailabilityMacros.h> |
|||
|
|||
/* strnlen() is not available on Mac OS < 10.7 */ |
|||
# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ |
|||
MAC_OS_X_VERSION_10_7) |
|||
|
|||
#define strnlen fdt_strnlen |
|||
|
|||
/*
|
|||
* fdt_strnlen: returns the length of a string or max_count - which ever is |
|||
* smallest. |
|||
* Input 1 string: the string whose size is to be determined |
|||
* Input 2 max_count: the maximum value returned by this function |
|||
* Output: length of the string or max_count (the smallest of the two) |
|||
*/ |
|||
static inline size_t fdt_strnlen(const char *string, size_t max_count) |
|||
{ |
|||
const char *p = memchr(string, 0, max_count); |
|||
return p ? p - string : max_count; |
|||
} |
|||
|
|||
#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < |
|||
MAC_OS_X_VERSION_10_7) */ |
|||
|
|||
#endif /* __APPLE__ */ |
|||
|
|||
#endif /* LIBFDT_ENV_H */ |
@ -0,0 +1,331 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
/*
|
|||
* Minimal sanity check for a read-only tree. fdt_ro_probe_() checks |
|||
* that the given buffer contains what appears to be a flattened |
|||
* device tree with sane information in its header. |
|||
*/ |
|||
int fdt_ro_probe_(const void *fdt) |
|||
{ |
|||
if (fdt_magic(fdt) == FDT_MAGIC) { |
|||
/* Complete tree */ |
|||
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) |
|||
return -FDT_ERR_BADVERSION; |
|||
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) |
|||
return -FDT_ERR_BADVERSION; |
|||
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) { |
|||
/* Unfinished sequential-write blob */ |
|||
if (fdt_size_dt_struct(fdt) == 0) |
|||
return -FDT_ERR_BADSTATE; |
|||
} else { |
|||
return -FDT_ERR_BADMAGIC; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) |
|||
{ |
|||
return (off >= hdrsize) && (off <= totalsize); |
|||
} |
|||
|
|||
static int check_block_(uint32_t hdrsize, uint32_t totalsize, |
|||
uint32_t base, uint32_t size) |
|||
{ |
|||
if (!check_off_(hdrsize, totalsize, base)) |
|||
return 0; /* block start out of bounds */ |
|||
if ((base + size) < base) |
|||
return 0; /* overflow */ |
|||
if (!check_off_(hdrsize, totalsize, base + size)) |
|||
return 0; /* block end out of bounds */ |
|||
return 1; |
|||
} |
|||
|
|||
size_t fdt_header_size_(uint32_t version) |
|||
{ |
|||
if (version <= 1) |
|||
return FDT_V1_SIZE; |
|||
else if (version <= 2) |
|||
return FDT_V2_SIZE; |
|||
else if (version <= 3) |
|||
return FDT_V3_SIZE; |
|||
else if (version <= 16) |
|||
return FDT_V16_SIZE; |
|||
else |
|||
return FDT_V17_SIZE; |
|||
} |
|||
|
|||
int fdt_check_header(const void *fdt) |
|||
{ |
|||
size_t hdrsize; |
|||
|
|||
if (fdt_magic(fdt) != FDT_MAGIC) |
|||
return -FDT_ERR_BADMAGIC; |
|||
hdrsize = fdt_header_size(fdt); |
|||
if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) |
|||
|| (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) |
|||
return -FDT_ERR_BADVERSION; |
|||
if (fdt_version(fdt) < fdt_last_comp_version(fdt)) |
|||
return -FDT_ERR_BADVERSION; |
|||
|
|||
if ((fdt_totalsize(fdt) < hdrsize) |
|||
|| (fdt_totalsize(fdt) > INT_MAX)) |
|||
return -FDT_ERR_TRUNCATED; |
|||
|
|||
/* Bounds check memrsv block */ |
|||
if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) |
|||
return -FDT_ERR_TRUNCATED; |
|||
|
|||
/* Bounds check structure block */ |
|||
if (fdt_version(fdt) < 17) { |
|||
if (!check_off_(hdrsize, fdt_totalsize(fdt), |
|||
fdt_off_dt_struct(fdt))) |
|||
return -FDT_ERR_TRUNCATED; |
|||
} else { |
|||
if (!check_block_(hdrsize, fdt_totalsize(fdt), |
|||
fdt_off_dt_struct(fdt), |
|||
fdt_size_dt_struct(fdt))) |
|||
return -FDT_ERR_TRUNCATED; |
|||
} |
|||
|
|||
/* Bounds check strings block */ |
|||
if (!check_block_(hdrsize, fdt_totalsize(fdt), |
|||
fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) |
|||
return -FDT_ERR_TRUNCATED; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) |
|||
{ |
|||
unsigned absoffset = offset + fdt_off_dt_struct(fdt); |
|||
|
|||
if ((absoffset < offset) |
|||
|| ((absoffset + len) < absoffset) |
|||
|| (absoffset + len) > fdt_totalsize(fdt)) |
|||
return NULL; |
|||
|
|||
if (fdt_version(fdt) >= 0x11) |
|||
if (((offset + len) < offset) |
|||
|| ((offset + len) > fdt_size_dt_struct(fdt))) |
|||
return NULL; |
|||
|
|||
return fdt_offset_ptr_(fdt, offset); |
|||
} |
|||
|
|||
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) |
|||
{ |
|||
const fdt32_t *tagp, *lenp; |
|||
uint32_t tag; |
|||
int offset = startoffset; |
|||
const char *p; |
|||
|
|||
*nextoffset = -FDT_ERR_TRUNCATED; |
|||
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); |
|||
if (!tagp) |
|||
return FDT_END; /* premature end */ |
|||
tag = fdt32_to_cpu(*tagp); |
|||
offset += FDT_TAGSIZE; |
|||
|
|||
*nextoffset = -FDT_ERR_BADSTRUCTURE; |
|||
switch (tag) { |
|||
case FDT_BEGIN_NODE: |
|||
/* skip name */ |
|||
do { |
|||
p = fdt_offset_ptr(fdt, offset++, 1); |
|||
} while (p && (*p != '\0')); |
|||
if (!p) |
|||
return FDT_END; /* premature end */ |
|||
break; |
|||
|
|||
case FDT_PROP: |
|||
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); |
|||
if (!lenp) |
|||
return FDT_END; /* premature end */ |
|||
/* skip-name offset, length and value */ |
|||
offset += sizeof(struct fdt_property) - FDT_TAGSIZE |
|||
+ fdt32_to_cpu(*lenp); |
|||
if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && |
|||
((offset - fdt32_to_cpu(*lenp)) % 8) != 0) |
|||
offset += 4; |
|||
break; |
|||
|
|||
case FDT_END: |
|||
case FDT_END_NODE: |
|||
case FDT_NOP: |
|||
break; |
|||
|
|||
default: |
|||
return FDT_END; |
|||
} |
|||
|
|||
if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) |
|||
return FDT_END; /* premature end */ |
|||
|
|||
*nextoffset = FDT_TAGALIGN(offset); |
|||
return tag; |
|||
} |
|||
|
|||
int fdt_check_node_offset_(const void *fdt, int offset) |
|||
{ |
|||
if ((offset < 0) || (offset % FDT_TAGSIZE) |
|||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_check_prop_offset_(const void *fdt, int offset) |
|||
{ |
|||
if ((offset < 0) || (offset % FDT_TAGSIZE) |
|||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_next_node(const void *fdt, int offset, int *depth) |
|||
{ |
|||
int nextoffset = 0; |
|||
uint32_t tag; |
|||
|
|||
if (offset >= 0) |
|||
if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) |
|||
return nextoffset; |
|||
|
|||
do { |
|||
offset = nextoffset; |
|||
tag = fdt_next_tag(fdt, offset, &nextoffset); |
|||
|
|||
switch (tag) { |
|||
case FDT_PROP: |
|||
case FDT_NOP: |
|||
break; |
|||
|
|||
case FDT_BEGIN_NODE: |
|||
if (depth) |
|||
(*depth)++; |
|||
break; |
|||
|
|||
case FDT_END_NODE: |
|||
if (depth && ((--(*depth)) < 0)) |
|||
return nextoffset; |
|||
break; |
|||
|
|||
case FDT_END: |
|||
if ((nextoffset >= 0) |
|||
|| ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) |
|||
return -FDT_ERR_NOTFOUND; |
|||
else |
|||
return nextoffset; |
|||
} |
|||
} while (tag != FDT_BEGIN_NODE); |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_first_subnode(const void *fdt, int offset) |
|||
{ |
|||
int depth = 0; |
|||
|
|||
offset = fdt_next_node(fdt, offset, &depth); |
|||
if (offset < 0 || depth != 1) |
|||
return -FDT_ERR_NOTFOUND; |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_next_subnode(const void *fdt, int offset) |
|||
{ |
|||
int depth = 1; |
|||
|
|||
/*
|
|||
* With respect to the parent, the depth of the next subnode will be |
|||
* the same as the last. |
|||
*/ |
|||
do { |
|||
offset = fdt_next_node(fdt, offset, &depth); |
|||
if (offset < 0 || depth < 1) |
|||
return -FDT_ERR_NOTFOUND; |
|||
} while (depth > 1); |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) |
|||
{ |
|||
int len = strlen(s) + 1; |
|||
const char *last = strtab + tabsize - len; |
|||
const char *p; |
|||
|
|||
for (p = strtab; p <= last; p++) |
|||
if (memcmp(p, s, len) == 0) |
|||
return p; |
|||
return NULL; |
|||
} |
|||
|
|||
int fdt_move(const void *fdt, void *buf, int bufsize) |
|||
{ |
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
if (fdt_totalsize(fdt) > bufsize) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
memmove(buf, fdt, fdt_totalsize(fdt)); |
|||
return 0; |
|||
} |
@ -0,0 +1,87 @@ |
|||
/*
|
|||
* libfdt - Flat Device Tree manipulation |
|||
* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au> |
|||
* Copyright (C) 2018 embedded brains GmbH |
|||
* |
|||
* 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" |
|||
|
|||
static int fdt_cells(const void *fdt, int nodeoffset, const char *name) |
|||
{ |
|||
const fdt32_t *c; |
|||
int val; |
|||
int len; |
|||
|
|||
c = fdt_getprop(fdt, nodeoffset, name, &len); |
|||
if (!c) |
|||
return 2; |
|||
|
|||
if (len != sizeof(*c)) |
|||
return -FDT_ERR_BADNCELLS; |
|||
|
|||
val = fdt32_to_cpu(*c); |
|||
if ((val <= 0) || (val > FDT_MAX_NCELLS)) |
|||
return -FDT_ERR_BADNCELLS; |
|||
|
|||
return val; |
|||
} |
|||
|
|||
int fdt_address_cells(const void *fdt, int nodeoffset) |
|||
{ |
|||
return fdt_cells(fdt, nodeoffset, "#address-cells"); |
|||
} |
|||
|
|||
int fdt_size_cells(const void *fdt, int nodeoffset) |
|||
{ |
|||
return fdt_cells(fdt, nodeoffset, "#size-cells"); |
|||
} |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
* libfdt - Flat Device Tree manipulation |
|||
* Copyright (C) 2012 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" |
|||
|
|||
int fdt_create_empty_tree(void *buf, int bufsize) |
|||
{ |
|||
int err; |
|||
|
|||
err = fdt_create(buf, bufsize); |
|||
if (err) |
|||
return err; |
|||
|
|||
err = fdt_finish_reservemap(buf); |
|||
if (err) |
|||
return err; |
|||
|
|||
err = fdt_begin_node(buf, ""); |
|||
if (err) |
|||
return err; |
|||
|
|||
err = fdt_end_node(buf); |
|||
if (err) |
|||
return err; |
|||
|
|||
err = fdt_finish(buf); |
|||
if (err) |
|||
return err; |
|||
|
|||
return fdt_open_into(buf, buf, bufsize); |
|||
} |
@ -0,0 +1,912 @@ |
|||
/*
|
|||
* libfdt - Flat Device Tree manipulation |
|||
* Copyright (C) 2016 Free Electrons |
|||
* Copyright (C) 2016 NextThing Co. |
|||
* |
|||
* 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" |
|||
|
|||
/**
|
|||
* overlay_get_target_phandle - retrieves the target phandle of a fragment |
|||
* @fdto: pointer to the device tree overlay blob |
|||
* @fragment: node offset of the fragment in the overlay |
|||
* |
|||
* overlay_get_target_phandle() retrieves the target phandle of an |
|||
* overlay fragment when that fragment uses a phandle (target |
|||
* property) instead of a path (target-path property). |
|||
* |
|||
* returns: |
|||
* the phandle pointed by the target property |
|||
* 0, if the phandle was not found |
|||
* -1, if the phandle was malformed |
|||
*/ |
|||
static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) |
|||
{ |
|||
const fdt32_t *val; |
|||
int len; |
|||
|
|||
val = fdt_getprop(fdto, fragment, "target", &len); |
|||
if (!val) |
|||
return 0; |
|||
|
|||
if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) |
|||
return (uint32_t)-1; |
|||
|
|||
return fdt32_to_cpu(*val); |
|||
} |
|||
|
|||
/**
|
|||
* overlay_get_target - retrieves the offset of a fragment's target |
|||
* @fdt: Base device tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* @fragment: node offset of the fragment in the overlay |
|||
* @pathp: pointer which receives the path of the target (or NULL) |
|||
* |
|||
* overlay_get_target() retrieves the target offset in the base |
|||
* device tree of a fragment, no matter how the actual targetting is |
|||
* done (through a phandle or a path) |
|||
* |
|||
* returns: |
|||
* the targetted node offset in the base device tree |
|||
* Negative error code on error |
|||
*/ |
|||
static int overlay_get_target(const void *fdt, const void *fdto, |
|||
int fragment, char const **pathp) |
|||
{ |
|||
uint32_t phandle; |
|||
const char *path = NULL; |
|||
int path_len = 0, ret; |
|||
|
|||
/* Try first to do a phandle based lookup */ |
|||
phandle = overlay_get_target_phandle(fdto, fragment); |
|||
if (phandle == (uint32_t)-1) |
|||
return -FDT_ERR_BADPHANDLE; |
|||
|
|||
/* no phandle, try path */ |
|||
if (!phandle) { |
|||
/* And then a path based lookup */ |
|||
path = fdt_getprop(fdto, fragment, "target-path", &path_len); |
|||
if (path) |
|||
ret = fdt_path_offset(fdt, path); |
|||
else |
|||
ret = path_len; |
|||
} else |
|||
ret = fdt_node_offset_by_phandle(fdt, phandle); |
|||
|
|||
/*
|
|||
* If we haven't found either a target or a |
|||
* target-path property in a node that contains a |
|||
* __overlay__ subnode (we wouldn't be called |
|||
* otherwise), consider it a improperly written |
|||
* overlay |
|||
*/ |
|||
if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) |
|||
ret = -FDT_ERR_BADOVERLAY; |
|||
|
|||
/* return on error */ |
|||
if (ret < 0) |
|||
return ret; |
|||
|
|||
/* return pointer to path (if available) */ |
|||
if (pathp) |
|||
*pathp = path ? path : NULL; |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_phandle_add_offset - Increases a phandle by an offset |
|||
* @fdt: Base device tree blob |
|||
* @node: Device tree overlay blob |
|||
* @name: Name of the property to modify (phandle or linux,phandle) |
|||
* @delta: offset to apply |
|||
* |
|||
* overlay_phandle_add_offset() increments a node phandle by a given |
|||
* offset. |
|||
* |
|||
* returns: |
|||
* 0 on success. |
|||
* Negative error code on error |
|||
*/ |
|||
static int overlay_phandle_add_offset(void *fdt, int node, |
|||
const char *name, uint32_t delta) |
|||
{ |
|||
const fdt32_t *val; |
|||
uint32_t adj_val; |
|||
int len; |
|||
|
|||
val = fdt_getprop(fdt, node, name, &len); |
|||
if (!val) |
|||
return len; |
|||
|
|||
if (len != sizeof(*val)) |
|||
return -FDT_ERR_BADPHANDLE; |
|||
|
|||
adj_val = fdt32_to_cpu(*val); |
|||
if ((adj_val + delta) < adj_val) |
|||
return -FDT_ERR_NOPHANDLES; |
|||
|
|||
adj_val += delta; |
|||
if (adj_val == (uint32_t)-1) |
|||
return -FDT_ERR_NOPHANDLES; |
|||
|
|||
return fdt_setprop_inplace_u32(fdt, node, name, adj_val); |
|||
} |
|||
|
|||
/**
|
|||
* overlay_adjust_node_phandles - Offsets the phandles of a node |
|||
* @fdto: Device tree overlay blob |
|||
* @node: Offset of the node we want to adjust |
|||
* @delta: Offset to shift the phandles of |
|||
* |
|||
* overlay_adjust_node_phandles() adds a constant to all the phandles |
|||
* of a given node. This is mainly use as part of the overlay |
|||
* application process, when we want to update all the overlay |
|||
* phandles to not conflict with the overlays of the base device tree. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_adjust_node_phandles(void *fdto, int node, |
|||
uint32_t delta) |
|||
{ |
|||
int child; |
|||
int ret; |
|||
|
|||
ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); |
|||
if (ret && ret != -FDT_ERR_NOTFOUND) |
|||
return ret; |
|||
|
|||
ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); |
|||
if (ret && ret != -FDT_ERR_NOTFOUND) |
|||
return ret; |
|||
|
|||
fdt_for_each_subnode(child, fdto, node) { |
|||
ret = overlay_adjust_node_phandles(fdto, child, delta); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_adjust_local_phandles - Adjust the phandles of a whole overlay |
|||
* @fdto: Device tree overlay blob |
|||
* @delta: Offset to shift the phandles of |
|||
* |
|||
* overlay_adjust_local_phandles() adds a constant to all the |
|||
* phandles of an overlay. This is mainly use as part of the overlay |
|||
* application process, when we want to update all the overlay |
|||
* phandles to not conflict with the overlays of the base device tree. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) |
|||
{ |
|||
/*
|
|||
* Start adjusting the phandles from the overlay root |
|||
*/ |
|||
return overlay_adjust_node_phandles(fdto, 0, delta); |
|||
} |
|||
|
|||
/**
|
|||
* overlay_update_local_node_references - Adjust the overlay references |
|||
* @fdto: Device tree overlay blob |
|||
* @tree_node: Node offset of the node to operate on |
|||
* @fixup_node: Node offset of the matching local fixups node |
|||
* @delta: Offset to shift the phandles of |
|||
* |
|||
* overlay_update_local_nodes_references() update the phandles |
|||
* pointing to a node within the device tree overlay by adding a |
|||
* constant delta. |
|||
* |
|||
* This is mainly used as part of a device tree application process, |
|||
* where you want the device tree overlays phandles to not conflict |
|||
* with the ones from the base device tree before merging them. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_update_local_node_references(void *fdto, |
|||
int tree_node, |
|||
int fixup_node, |
|||
uint32_t delta) |
|||
{ |
|||
int fixup_prop; |
|||
int fixup_child; |
|||
int ret; |
|||
|
|||
fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { |
|||
const fdt32_t *fixup_val; |
|||
const char *tree_val; |
|||
const char *name; |
|||
int fixup_len; |
|||
int tree_len; |
|||
int i; |
|||
|
|||
fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, |
|||
&name, &fixup_len); |
|||
if (!fixup_val) |
|||
return fixup_len; |
|||
|
|||
if (fixup_len % sizeof(uint32_t)) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); |
|||
if (!tree_val) { |
|||
if (tree_len == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
return tree_len; |
|||
} |
|||
|
|||
for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { |
|||
fdt32_t adj_val; |
|||
uint32_t poffset; |
|||
|
|||
poffset = fdt32_to_cpu(fixup_val[i]); |
|||
|
|||
/*
|
|||
* phandles to fixup can be unaligned. |
|||
* |
|||
* Use a memcpy for the architectures that do |
|||
* not support unaligned accesses. |
|||
*/ |
|||
memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); |
|||
|
|||
adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); |
|||
|
|||
ret = fdt_setprop_inplace_namelen_partial(fdto, |
|||
tree_node, |
|||
name, |
|||
strlen(name), |
|||
poffset, |
|||
&adj_val, |
|||
sizeof(adj_val)); |
|||
if (ret == -FDT_ERR_NOSPACE) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
if (ret) |
|||
return ret; |
|||
} |
|||
} |
|||
|
|||
fdt_for_each_subnode(fixup_child, fdto, fixup_node) { |
|||
const char *fixup_child_name = fdt_get_name(fdto, fixup_child, |
|||
NULL); |
|||
int tree_child; |
|||
|
|||
tree_child = fdt_subnode_offset(fdto, tree_node, |
|||
fixup_child_name); |
|||
if (tree_child == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
if (tree_child < 0) |
|||
return tree_child; |
|||
|
|||
ret = overlay_update_local_node_references(fdto, |
|||
tree_child, |
|||
fixup_child, |
|||
delta); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_update_local_references - Adjust the overlay references |
|||
* @fdto: Device tree overlay blob |
|||
* @delta: Offset to shift the phandles of |
|||
* |
|||
* overlay_update_local_references() update all the phandles pointing |
|||
* to a node within the device tree overlay by adding a constant |
|||
* delta to not conflict with the base overlay. |
|||
* |
|||
* This is mainly used as part of a device tree application process, |
|||
* where you want the device tree overlays phandles to not conflict |
|||
* with the ones from the base device tree before merging them. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_update_local_references(void *fdto, uint32_t delta) |
|||
{ |
|||
int fixups; |
|||
|
|||
fixups = fdt_path_offset(fdto, "/__local_fixups__"); |
|||
if (fixups < 0) { |
|||
/* There's no local phandles to adjust, bail out */ |
|||
if (fixups == -FDT_ERR_NOTFOUND) |
|||
return 0; |
|||
|
|||
return fixups; |
|||
} |
|||
|
|||
/*
|
|||
* Update our local references from the root of the tree |
|||
*/ |
|||
return overlay_update_local_node_references(fdto, 0, fixups, |
|||
delta); |
|||
} |
|||
|
|||
/**
|
|||
* overlay_fixup_one_phandle - Set an overlay phandle to the base one |
|||
* @fdt: Base Device Tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* @symbols_off: Node offset of the symbols node in the base device tree |
|||
* @path: Path to a node holding a phandle in the overlay |
|||
* @path_len: number of path characters to consider |
|||
* @name: Name of the property holding the phandle reference in the overlay |
|||
* @name_len: number of name characters to consider |
|||
* @poffset: Offset within the overlay property where the phandle is stored |
|||
* @label: Label of the node referenced by the phandle |
|||
* |
|||
* overlay_fixup_one_phandle() resolves an overlay phandle pointing to |
|||
* a node in the base device tree. |
|||
* |
|||
* This is part of the device tree overlay application process, when |
|||
* you want all the phandles in the overlay to point to the actual |
|||
* base dt nodes. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_fixup_one_phandle(void *fdt, void *fdto, |
|||
int symbols_off, |
|||
const char *path, uint32_t path_len, |
|||
const char *name, uint32_t name_len, |
|||
int poffset, const char *label) |
|||
{ |
|||
const char *symbol_path; |
|||
uint32_t phandle; |
|||
fdt32_t phandle_prop; |
|||
int symbol_off, fixup_off; |
|||
int prop_len; |
|||
|
|||
if (symbols_off < 0) |
|||
return symbols_off; |
|||
|
|||
symbol_path = fdt_getprop(fdt, symbols_off, label, |
|||
&prop_len); |
|||
if (!symbol_path) |
|||
return prop_len; |
|||
|
|||
symbol_off = fdt_path_offset(fdt, symbol_path); |
|||
if (symbol_off < 0) |
|||
return symbol_off; |
|||
|
|||
phandle = fdt_get_phandle(fdt, symbol_off); |
|||
if (!phandle) |
|||
return -FDT_ERR_NOTFOUND; |
|||
|
|||
fixup_off = fdt_path_offset_namelen(fdto, path, path_len); |
|||
if (fixup_off == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
if (fixup_off < 0) |
|||
return fixup_off; |
|||
|
|||
phandle_prop = cpu_to_fdt32(phandle); |
|||
return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, |
|||
name, name_len, poffset, |
|||
&phandle_prop, |
|||
sizeof(phandle_prop)); |
|||
}; |
|||
|
|||
/**
|
|||
* overlay_fixup_phandle - Set an overlay phandle to the base one |
|||
* @fdt: Base Device Tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* @symbols_off: Node offset of the symbols node in the base device tree |
|||
* @property: Property offset in the overlay holding the list of fixups |
|||
* |
|||
* overlay_fixup_phandle() resolves all the overlay phandles pointed |
|||
* to in a __fixups__ property, and updates them to match the phandles |
|||
* in use in the base device tree. |
|||
* |
|||
* This is part of the device tree overlay application process, when |
|||
* you want all the phandles in the overlay to point to the actual |
|||
* base dt nodes. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, |
|||
int property) |
|||
{ |
|||
const char *value; |
|||
const char *label; |
|||
int len; |
|||
|
|||
value = fdt_getprop_by_offset(fdto, property, |
|||
&label, &len); |
|||
if (!value) { |
|||
if (len == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_INTERNAL; |
|||
|
|||
return len; |
|||
} |
|||
|
|||
do { |
|||
const char *path, *name, *fixup_end; |
|||
const char *fixup_str = value; |
|||
uint32_t path_len, name_len; |
|||
uint32_t fixup_len; |
|||
char *sep, *endptr; |
|||
int poffset, ret; |
|||
|
|||
fixup_end = memchr(value, '\0', len); |
|||
if (!fixup_end) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
fixup_len = fixup_end - fixup_str; |
|||
|
|||
len -= fixup_len + 1; |
|||
value += fixup_len + 1; |
|||
|
|||
path = fixup_str; |
|||
sep = memchr(fixup_str, ':', fixup_len); |
|||
if (!sep || *sep != ':') |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
path_len = sep - path; |
|||
if (path_len == (fixup_len - 1)) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
fixup_len -= path_len + 1; |
|||
name = sep + 1; |
|||
sep = memchr(name, ':', fixup_len); |
|||
if (!sep || *sep != ':') |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
name_len = sep - name; |
|||
if (!name_len) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
poffset = strtoul(sep + 1, &endptr, 10); |
|||
if ((*endptr != '\0') || (endptr <= (sep + 1))) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, |
|||
path, path_len, name, name_len, |
|||
poffset, label); |
|||
if (ret) |
|||
return ret; |
|||
} while (len > 0); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_fixup_phandles - Resolve the overlay phandles to the base |
|||
* device tree |
|||
* @fdt: Base Device Tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* |
|||
* overlay_fixup_phandles() resolves all the overlay phandles pointing |
|||
* to nodes in the base device tree. |
|||
* |
|||
* This is one of the steps of the device tree overlay application |
|||
* process, when you want all the phandles in the overlay to point to |
|||
* the actual base dt nodes. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_fixup_phandles(void *fdt, void *fdto) |
|||
{ |
|||
int fixups_off, symbols_off; |
|||
int property; |
|||
|
|||
/* We can have overlays without any fixups */ |
|||
fixups_off = fdt_path_offset(fdto, "/__fixups__"); |
|||
if (fixups_off == -FDT_ERR_NOTFOUND) |
|||
return 0; /* nothing to do */ |
|||
if (fixups_off < 0) |
|||
return fixups_off; |
|||
|
|||
/* And base DTs without symbols */ |
|||
symbols_off = fdt_path_offset(fdt, "/__symbols__"); |
|||
if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) |
|||
return symbols_off; |
|||
|
|||
fdt_for_each_property_offset(property, fdto, fixups_off) { |
|||
int ret; |
|||
|
|||
ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_apply_node - Merges a node into the base device tree |
|||
* @fdt: Base Device Tree blob |
|||
* @target: Node offset in the base device tree to apply the fragment to |
|||
* @fdto: Device tree overlay blob |
|||
* @node: Node offset in the overlay holding the changes to merge |
|||
* |
|||
* overlay_apply_node() merges a node into a target base device tree |
|||
* node pointed. |
|||
* |
|||
* This is part of the final step in the device tree overlay |
|||
* application process, when all the phandles have been adjusted and |
|||
* resolved and you just have to merge overlay into the base device |
|||
* tree. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_apply_node(void *fdt, int target, |
|||
void *fdto, int node) |
|||
{ |
|||
int property; |
|||
int subnode; |
|||
|
|||
fdt_for_each_property_offset(property, fdto, node) { |
|||
const char *name; |
|||
const void *prop; |
|||
int prop_len; |
|||
int ret; |
|||
|
|||
prop = fdt_getprop_by_offset(fdto, property, &name, |
|||
&prop_len); |
|||
if (prop_len == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_INTERNAL; |
|||
if (prop_len < 0) |
|||
return prop_len; |
|||
|
|||
ret = fdt_setprop(fdt, target, name, prop, prop_len); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
fdt_for_each_subnode(subnode, fdto, node) { |
|||
const char *name = fdt_get_name(fdto, subnode, NULL); |
|||
int nnode; |
|||
int ret; |
|||
|
|||
nnode = fdt_add_subnode(fdt, target, name); |
|||
if (nnode == -FDT_ERR_EXISTS) { |
|||
nnode = fdt_subnode_offset(fdt, target, name); |
|||
if (nnode == -FDT_ERR_NOTFOUND) |
|||
return -FDT_ERR_INTERNAL; |
|||
} |
|||
|
|||
if (nnode < 0) |
|||
return nnode; |
|||
|
|||
ret = overlay_apply_node(fdt, nnode, fdto, subnode); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_merge - Merge an overlay into its base device tree |
|||
* @fdt: Base Device Tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* |
|||
* overlay_merge() merges an overlay into its base device tree. |
|||
* |
|||
* This is the next to last step in the device tree overlay application |
|||
* process, when all the phandles have been adjusted and resolved and |
|||
* you just have to merge overlay into the base device tree. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_merge(void *fdt, void *fdto) |
|||
{ |
|||
int fragment; |
|||
|
|||
fdt_for_each_subnode(fragment, fdto, 0) { |
|||
int overlay; |
|||
int target; |
|||
int ret; |
|||
|
|||
/*
|
|||
* Each fragments will have an __overlay__ node. If |
|||
* they don't, it's not supposed to be merged |
|||
*/ |
|||
overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); |
|||
if (overlay == -FDT_ERR_NOTFOUND) |
|||
continue; |
|||
|
|||
if (overlay < 0) |
|||
return overlay; |
|||
|
|||
target = overlay_get_target(fdt, fdto, fragment, NULL); |
|||
if (target < 0) |
|||
return target; |
|||
|
|||
ret = overlay_apply_node(fdt, target, fdto, overlay); |
|||
if (ret) |
|||
return ret; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int get_path_len(const void *fdt, int nodeoffset) |
|||
{ |
|||
int len = 0, namelen; |
|||
const char *name; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
for (;;) { |
|||
name = fdt_get_name(fdt, nodeoffset, &namelen); |
|||
if (!name) |
|||
return namelen; |
|||
|
|||
/* root? we're done */ |
|||
if (namelen == 0) |
|||
break; |
|||
|
|||
nodeoffset = fdt_parent_offset(fdt, nodeoffset); |
|||
if (nodeoffset < 0) |
|||
return nodeoffset; |
|||
len += namelen + 1; |
|||
} |
|||
|
|||
/* in case of root pretend it's "/" */ |
|||
if (len == 0) |
|||
len++; |
|||
return len; |
|||
} |
|||
|
|||
/**
|
|||
* overlay_symbol_update - Update the symbols of base tree after a merge |
|||
* @fdt: Base Device Tree blob |
|||
* @fdto: Device tree overlay blob |
|||
* |
|||
* overlay_symbol_update() updates the symbols of the base tree with the |
|||
* symbols of the applied overlay |
|||
* |
|||
* This is the last step in the device tree overlay application |
|||
* process, allowing the reference of overlay symbols by subsequent |
|||
* overlay operations. |
|||
* |
|||
* returns: |
|||
* 0 on success |
|||
* Negative error code on failure |
|||
*/ |
|||
static int overlay_symbol_update(void *fdt, void *fdto) |
|||
{ |
|||
int root_sym, ov_sym, prop, path_len, fragment, target; |
|||
int len, frag_name_len, ret, rel_path_len; |
|||
const char *s, *e; |
|||
const char *path; |
|||
const char *name; |
|||
const char *frag_name; |
|||
const char *rel_path; |
|||
const char *target_path; |
|||
char *buf; |
|||
void *p; |
|||
|
|||
ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); |
|||
|
|||
/* if no overlay symbols exist no problem */ |
|||
if (ov_sym < 0) |
|||
return 0; |
|||
|
|||
root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); |
|||
|
|||
/* it no root symbols exist we should create them */ |
|||
if (root_sym == -FDT_ERR_NOTFOUND) |
|||
root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); |
|||
|
|||
/* any error is fatal now */ |
|||
if (root_sym < 0) |
|||
return root_sym; |
|||
|
|||
/* iterate over each overlay symbol */ |
|||
fdt_for_each_property_offset(prop, fdto, ov_sym) { |
|||
path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); |
|||
if (!path) |
|||
return path_len; |
|||
|
|||
/* verify it's a string property (terminated by a single \0) */ |
|||
if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) |
|||
return -FDT_ERR_BADVALUE; |
|||
|
|||
/* keep end marker to avoid strlen() */ |
|||
e = path + path_len; |
|||
|
|||
/* format: /<fragment-name>/__overlay__/<relative-subnode-path> */ |
|||
|
|||
if (*path != '/') |
|||
return -FDT_ERR_BADVALUE; |
|||
|
|||
/* get fragment name first */ |
|||
s = strchr(path + 1, '/'); |
|||
if (!s) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
frag_name = path + 1; |
|||
frag_name_len = s - path - 1; |
|||
|
|||
/* verify format; safe since "s" lies in \0 terminated prop */ |
|||
len = sizeof("/__overlay__/") - 1; |
|||
if ((e - s) < len || memcmp(s, "/__overlay__/", len)) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
rel_path = s + len; |
|||
rel_path_len = e - rel_path; |
|||
|
|||
/* find the fragment index in which the symbol lies */ |
|||
ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, |
|||
frag_name_len); |
|||
/* not found? */ |
|||
if (ret < 0) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
fragment = ret; |
|||
|
|||
/* an __overlay__ subnode must exist */ |
|||
ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); |
|||
if (ret < 0) |
|||
return -FDT_ERR_BADOVERLAY; |
|||
|
|||
/* get the target of the fragment */ |
|||
ret = overlay_get_target(fdt, fdto, fragment, &target_path); |
|||
if (ret < 0) |
|||
return ret; |
|||
target = ret; |
|||
|
|||
/* if we have a target path use */ |
|||
if (!target_path) { |
|||
ret = get_path_len(fdt, target); |
|||
if (ret < 0) |
|||
return ret; |
|||
len = ret; |
|||
} else { |
|||
len = strlen(target_path); |
|||
} |
|||
|
|||
ret = fdt_setprop_placeholder(fdt, root_sym, name, |
|||
len + (len > 1) + rel_path_len + 1, &p); |
|||
if (ret < 0) |
|||
return ret; |
|||
|
|||
if (!target_path) { |
|||
/* again in case setprop_placeholder changed it */ |
|||
ret = overlay_get_target(fdt, fdto, fragment, &target_path); |
|||
if (ret < 0) |
|||
return ret; |
|||
target = ret; |
|||
} |
|||
|
|||
buf = p; |
|||
if (len > 1) { /* target is not root */ |
|||
if (!target_path) { |
|||
ret = fdt_get_path(fdt, target, buf, len + 1); |
|||
if (ret < 0) |
|||
return ret; |
|||
} else |
|||
memcpy(buf, target_path, len + 1); |
|||
|
|||
} else |
|||
len--; |
|||
|
|||
buf[len] = '/'; |
|||
memcpy(buf + len + 1, rel_path, rel_path_len); |
|||
buf[len + 1 + rel_path_len] = '\0'; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int fdt_overlay_apply(void *fdt, void *fdto) |
|||
{ |
|||
uint32_t delta = fdt_get_max_phandle(fdt); |
|||
int ret; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
FDT_RO_PROBE(fdto); |
|||
|
|||
ret = overlay_adjust_local_phandles(fdto, delta); |
|||
if (ret) |
|||
goto err; |
|||
|
|||
ret = overlay_update_local_references(fdto, delta); |
|||
if (ret) |
|||
goto err; |
|||
|
|||
ret = overlay_fixup_phandles(fdt, fdto); |
|||
if (ret) |
|||
goto err; |
|||
|
|||
ret = overlay_merge(fdt, fdto); |
|||
if (ret) |
|||
goto err; |
|||
|
|||
ret = overlay_symbol_update(fdt, fdto); |
|||
if (ret) |
|||
goto err; |
|||
|
|||
/*
|
|||
* The overlay has been damaged, erase its magic. |
|||
*/ |
|||
fdt_set_magic(fdto, ~0); |
|||
|
|||
return 0; |
|||
|
|||
err: |
|||
/*
|
|||
* The overlay might have been damaged, erase its magic. |
|||
*/ |
|||
fdt_set_magic(fdto, ~0); |
|||
|
|||
/*
|
|||
* The base device tree might have been damaged, erase its |
|||
* magic. |
|||
*/ |
|||
fdt_set_magic(fdt, ~0); |
|||
|
|||
return ret; |
|||
} |
@ -0,0 +1,922 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
static int fdt_nodename_eq_(const void *fdt, int offset, |
|||
const char *s, int len) |
|||
{ |
|||
int olen; |
|||
const char *p = fdt_get_name(fdt, offset, &olen); |
|||
|
|||
if (!p || olen < len) |
|||
/* short match */ |
|||
return 0; |
|||
|
|||
if (memcmp(p, s, len) != 0) |
|||
return 0; |
|||
|
|||
if (p[len] == '\0') |
|||
return 1; |
|||
else if (!memchr(s, '@', len) && (p[len] == '@')) |
|||
return 1; |
|||
else |
|||
return 0; |
|||
} |
|||
|
|||
const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) |
|||
{ |
|||
uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); |
|||
size_t len; |
|||
int err; |
|||
const char *s, *n; |
|||
|
|||
err = fdt_ro_probe_(fdt); |
|||
if (err != 0) |
|||
goto fail; |
|||
|
|||
err = -FDT_ERR_BADOFFSET; |
|||
if (absoffset >= fdt_totalsize(fdt)) |
|||
goto fail; |
|||
len = fdt_totalsize(fdt) - absoffset; |
|||
|
|||
if (fdt_magic(fdt) == FDT_MAGIC) { |
|||
if (stroffset < 0) |
|||
goto fail; |
|||
if (fdt_version(fdt) >= 17) { |
|||
if (stroffset >= fdt_size_dt_strings(fdt)) |
|||
goto fail; |
|||
if ((fdt_size_dt_strings(fdt) - stroffset) < len) |
|||
len = fdt_size_dt_strings(fdt) - stroffset; |
|||
} |
|||
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) { |
|||
if ((stroffset >= 0) |
|||
|| (stroffset < -fdt_size_dt_strings(fdt))) |
|||
goto fail; |
|||
if ((-stroffset) < len) |
|||
len = -stroffset; |
|||
} else { |
|||
err = -FDT_ERR_INTERNAL; |
|||
goto fail; |
|||
} |
|||
|
|||
s = (const char *)fdt + absoffset; |
|||
n = memchr(s, '\0', len); |
|||
if (!n) { |
|||
/* missing terminating NULL */ |
|||
err = -FDT_ERR_TRUNCATED; |
|||
goto fail; |
|||
} |
|||
|
|||
if (lenp) |
|||
*lenp = n - s; |
|||
return s; |
|||
|
|||
fail: |
|||
if (lenp) |
|||
*lenp = err; |
|||
return NULL; |
|||
} |
|||
|
|||
const char *fdt_string(const void *fdt, int stroffset) |
|||
{ |
|||
return fdt_get_string(fdt, stroffset, NULL); |
|||
} |
|||
|
|||
static int fdt_string_eq_(const void *fdt, int stroffset, |
|||
const char *s, int len) |
|||
{ |
|||
int slen; |
|||
const char *p = fdt_get_string(fdt, stroffset, &slen); |
|||
|
|||
return p && (slen == len) && (memcmp(p, s, len) == 0); |
|||
} |
|||
|
|||
uint32_t fdt_get_max_phandle(const void *fdt) |
|||
{ |
|||
uint32_t max_phandle = 0; |
|||
int offset; |
|||
|
|||
for (offset = fdt_next_node(fdt, -1, NULL);; |
|||
offset = fdt_next_node(fdt, offset, NULL)) { |
|||
uint32_t phandle; |
|||
|
|||
if (offset == -FDT_ERR_NOTFOUND) |
|||
return max_phandle; |
|||
|
|||
if (offset < 0) |
|||
return (uint32_t)-1; |
|||
|
|||
phandle = fdt_get_phandle(fdt, offset); |
|||
if (phandle == (uint32_t)-1) |
|||
continue; |
|||
|
|||
if (phandle > max_phandle) |
|||
max_phandle = phandle; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) |
|||
{ |
|||
int offset = n * sizeof(struct fdt_reserve_entry); |
|||
int absoffset = fdt_off_mem_rsvmap(fdt) + offset; |
|||
|
|||
if (absoffset < fdt_off_mem_rsvmap(fdt)) |
|||
return NULL; |
|||
if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) |
|||
return NULL; |
|||
return fdt_mem_rsv_(fdt, n); |
|||
} |
|||
|
|||
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) |
|||
{ |
|||
const struct fdt_reserve_entry *re; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
re = fdt_mem_rsv(fdt, n); |
|||
if (!re) |
|||
return -FDT_ERR_BADOFFSET; |
|||
|
|||
*address = fdt64_ld(&re->address); |
|||
*size = fdt64_ld(&re->size); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_num_mem_rsv(const void *fdt) |
|||
{ |
|||
int i; |
|||
const struct fdt_reserve_entry *re; |
|||
|
|||
for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { |
|||
if (fdt64_ld(&re->size) == 0) |
|||
return i; |
|||
} |
|||
return -FDT_ERR_TRUNCATED; |
|||
} |
|||
|
|||
static int nextprop_(const void *fdt, int offset) |
|||
{ |
|||
uint32_t tag; |
|||
int nextoffset; |
|||
|
|||
do { |
|||
tag = fdt_next_tag(fdt, offset, &nextoffset); |
|||
|
|||
switch (tag) { |
|||
case FDT_END: |
|||
if (nextoffset >= 0) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
else |
|||
return nextoffset; |
|||
|
|||
case FDT_PROP: |
|||
return offset; |
|||
} |
|||
offset = nextoffset; |
|||
} while (tag == FDT_NOP); |
|||
|
|||
return -FDT_ERR_NOTFOUND; |
|||
} |
|||
|
|||
int fdt_subnode_offset_namelen(const void *fdt, int offset, |
|||
const char *name, int namelen) |
|||
{ |
|||
int depth; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
for (depth = 0; |
|||
(offset >= 0) && (depth >= 0); |
|||
offset = fdt_next_node(fdt, offset, &depth)) |
|||
if ((depth == 1) |
|||
&& fdt_nodename_eq_(fdt, offset, name, namelen)) |
|||
return offset; |
|||
|
|||
if (depth < 0) |
|||
return -FDT_ERR_NOTFOUND; |
|||
return offset; /* error */ |
|||
} |
|||
|
|||
int fdt_subnode_offset(const void *fdt, int parentoffset, |
|||
const char *name) |
|||
{ |
|||
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); |
|||
} |
|||
|
|||
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) |
|||
{ |
|||
const char *end = path + namelen; |
|||
const char *p = path; |
|||
int offset = 0; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
/* see if we have an alias */ |
|||
if (*path != '/') { |
|||
const char *q = memchr(path, '/', end - p); |
|||
|
|||
if (!q) |
|||
q = end; |
|||
|
|||
p = fdt_get_alias_namelen(fdt, p, q - p); |
|||
if (!p) |
|||
return -FDT_ERR_BADPATH; |
|||
offset = fdt_path_offset(fdt, p); |
|||
|
|||
p = q; |
|||
} |
|||
|
|||
while (p < end) { |
|||
const char *q; |
|||
|
|||
while (*p == '/') { |
|||
p++; |
|||
if (p == end) |
|||
return offset; |
|||
} |
|||
q = memchr(p, '/', end - p); |
|||
if (! q) |
|||
q = end; |
|||
|
|||
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); |
|||
if (offset < 0) |
|||
return offset; |
|||
|
|||
p = q; |
|||
} |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_path_offset(const void *fdt, const char *path) |
|||
{ |
|||
return fdt_path_offset_namelen(fdt, path, strlen(path)); |
|||
} |
|||
|
|||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) |
|||
{ |
|||
const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); |
|||
const char *nameptr; |
|||
int err; |
|||
|
|||
if (((err = fdt_ro_probe_(fdt)) != 0) |
|||
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) |
|||
goto fail; |
|||
|
|||
nameptr = nh->name; |
|||
|
|||
if (fdt_version(fdt) < 0x10) { |
|||
/*
|
|||
* For old FDT versions, match the naming conventions of V16: |
|||
* give only the leaf name (after all /). The actual tree |
|||
* contents are loosely checked. |
|||
*/ |
|||
const char *leaf; |
|||
leaf = strrchr(nameptr, '/'); |
|||
if (leaf == NULL) { |
|||
err = -FDT_ERR_BADSTRUCTURE; |
|||
goto fail; |
|||
} |
|||
nameptr = leaf+1; |
|||
} |
|||
|
|||
if (len) |
|||
*len = strlen(nameptr); |
|||
|
|||
return nameptr; |
|||
|
|||
fail: |
|||
if (len) |
|||
*len = err; |
|||
return NULL; |
|||
} |
|||
|
|||
int fdt_first_property_offset(const void *fdt, int nodeoffset) |
|||
{ |
|||
int offset; |
|||
|
|||
if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) |
|||
return offset; |
|||
|
|||
return nextprop_(fdt, offset); |
|||
} |
|||
|
|||
int fdt_next_property_offset(const void *fdt, int offset) |
|||
{ |
|||
if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) |
|||
return offset; |
|||
|
|||
return nextprop_(fdt, offset); |
|||
} |
|||
|
|||
static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, |
|||
int offset, |
|||
int *lenp) |
|||
{ |
|||
int err; |
|||
const struct fdt_property *prop; |
|||
|
|||
if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { |
|||
if (lenp) |
|||
*lenp = err; |
|||
return NULL; |
|||
} |
|||
|
|||
prop = fdt_offset_ptr_(fdt, offset); |
|||
|
|||
if (lenp) |
|||
*lenp = fdt32_ld(&prop->len); |
|||
|
|||
return prop; |
|||
} |
|||
|
|||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt, |
|||
int offset, |
|||
int *lenp) |
|||
{ |
|||
/* Prior to version 16, properties may need realignment
|
|||
* and this API does not work. fdt_getprop_*() will, however. */ |
|||
|
|||
if (fdt_version(fdt) < 0x10) { |
|||
if (lenp) |
|||
*lenp = -FDT_ERR_BADVERSION; |
|||
return NULL; |
|||
} |
|||
|
|||
return fdt_get_property_by_offset_(fdt, offset, lenp); |
|||
} |
|||
|
|||
static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, |
|||
int offset, |
|||
const char *name, |
|||
int namelen, |
|||
int *lenp, |
|||
int *poffset) |
|||
{ |
|||
for (offset = fdt_first_property_offset(fdt, offset); |
|||
(offset >= 0); |
|||
(offset = fdt_next_property_offset(fdt, offset))) { |
|||
const struct fdt_property *prop; |
|||
|
|||
if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { |
|||
offset = -FDT_ERR_INTERNAL; |
|||
break; |
|||
} |
|||
if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), |
|||
name, namelen)) { |
|||
if (poffset) |
|||
*poffset = offset; |
|||
return prop; |
|||
} |
|||
} |
|||
|
|||
if (lenp) |
|||
*lenp = offset; |
|||
return NULL; |
|||
} |
|||
|
|||
|
|||
const struct fdt_property *fdt_get_property_namelen(const void *fdt, |
|||
int offset, |
|||
const char *name, |
|||
int namelen, int *lenp) |
|||
{ |
|||
/* Prior to version 16, properties may need realignment
|
|||
* and this API does not work. fdt_getprop_*() will, however. */ |
|||
if (fdt_version(fdt) < 0x10) { |
|||
if (lenp) |
|||
*lenp = -FDT_ERR_BADVERSION; |
|||
return NULL; |
|||
} |
|||
|
|||
return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, |
|||
NULL); |
|||
} |
|||
|
|||
|
|||
const struct fdt_property *fdt_get_property(const void *fdt, |
|||
int nodeoffset, |
|||
const char *name, int *lenp) |
|||
{ |
|||
return fdt_get_property_namelen(fdt, nodeoffset, name, |
|||
strlen(name), lenp); |
|||
} |
|||
|
|||
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, |
|||
const char *name, int namelen, int *lenp) |
|||
{ |
|||
int poffset; |
|||
const struct fdt_property *prop; |
|||
|
|||
prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, |
|||
&poffset); |
|||
if (!prop) |
|||
return NULL; |
|||
|
|||
/* Handle realignment */ |
|||
if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && |
|||
fdt32_ld(&prop->len) >= 8) |
|||
return prop->data + 4; |
|||
return prop->data; |
|||
} |
|||
|
|||
const void *fdt_getprop_by_offset(const void *fdt, int offset, |
|||
const char **namep, int *lenp) |
|||
{ |
|||
const struct fdt_property *prop; |
|||
|
|||
prop = fdt_get_property_by_offset_(fdt, offset, lenp); |
|||
if (!prop) |
|||
return NULL; |
|||
if (namep) { |
|||
const char *name; |
|||
int namelen; |
|||
name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), |
|||
&namelen); |
|||
if (!name) { |
|||
if (lenp) |
|||
*lenp = namelen; |
|||
return NULL; |
|||
} |
|||
*namep = name; |
|||
} |
|||
|
|||
/* Handle realignment */ |
|||
if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && |
|||
fdt32_ld(&prop->len) >= 8) |
|||
return prop->data + 4; |
|||
return prop->data; |
|||
} |
|||
|
|||
const void *fdt_getprop(const void *fdt, int nodeoffset, |
|||
const char *name, int *lenp) |
|||
{ |
|||
return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); |
|||
} |
|||
|
|||
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) |
|||
{ |
|||
const fdt32_t *php; |
|||
int len; |
|||
|
|||
/* FIXME: This is a bit sub-optimal, since we potentially scan
|
|||
* over all the properties twice. */ |
|||
php = fdt_getprop(fdt, nodeoffset, "phandle", &len); |
|||
if (!php || (len != sizeof(*php))) { |
|||
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); |
|||
if (!php || (len != sizeof(*php))) |
|||
return 0; |
|||
} |
|||
|
|||
return fdt32_ld(php); |
|||
} |
|||
|
|||
const char *fdt_get_alias_namelen(const void *fdt, |
|||
const char *name, int namelen) |
|||
{ |
|||
int aliasoffset; |
|||
|
|||
aliasoffset = fdt_path_offset(fdt, "/aliases"); |
|||
if (aliasoffset < 0) |
|||
return NULL; |
|||
|
|||
return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); |
|||
} |
|||
|
|||
const char *fdt_get_alias(const void *fdt, const char *name) |
|||
{ |
|||
return fdt_get_alias_namelen(fdt, name, strlen(name)); |
|||
} |
|||
|
|||
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) |
|||
{ |
|||
int pdepth = 0, p = 0; |
|||
int offset, depth, namelen; |
|||
const char *name; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
if (buflen < 2) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
for (offset = 0, depth = 0; |
|||
(offset >= 0) && (offset <= nodeoffset); |
|||
offset = fdt_next_node(fdt, offset, &depth)) { |
|||
while (pdepth > depth) { |
|||
do { |
|||
p--; |
|||
} while (buf[p-1] != '/'); |
|||
pdepth--; |
|||
} |
|||
|
|||
if (pdepth >= depth) { |
|||
name = fdt_get_name(fdt, offset, &namelen); |
|||
if (!name) |
|||
return namelen; |
|||
if ((p + namelen + 1) <= buflen) { |
|||
memcpy(buf + p, name, namelen); |
|||
p += namelen; |
|||
buf[p++] = '/'; |
|||
pdepth++; |
|||
} |
|||
} |
|||
|
|||
if (offset == nodeoffset) { |
|||
if (pdepth < (depth + 1)) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
if (p > 1) /* special case so that root path is "/", not "" */ |
|||
p--; |
|||
buf[p] = '\0'; |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
else if (offset == -FDT_ERR_BADOFFSET) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
|
|||
return offset; /* error from fdt_next_node() */ |
|||
} |
|||
|
|||
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, |
|||
int supernodedepth, int *nodedepth) |
|||
{ |
|||
int offset, depth; |
|||
int supernodeoffset = -FDT_ERR_INTERNAL; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
if (supernodedepth < 0) |
|||
return -FDT_ERR_NOTFOUND; |
|||
|
|||
for (offset = 0, depth = 0; |
|||
(offset >= 0) && (offset <= nodeoffset); |
|||
offset = fdt_next_node(fdt, offset, &depth)) { |
|||
if (depth == supernodedepth) |
|||
supernodeoffset = offset; |
|||
|
|||
if (offset == nodeoffset) { |
|||
if (nodedepth) |
|||
*nodedepth = depth; |
|||
|
|||
if (supernodedepth > depth) |
|||
return -FDT_ERR_NOTFOUND; |
|||
else |
|||
return supernodeoffset; |
|||
} |
|||
} |
|||
|
|||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
else if (offset == -FDT_ERR_BADOFFSET) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
|
|||
return offset; /* error from fdt_next_node() */ |
|||
} |
|||
|
|||
int fdt_node_depth(const void *fdt, int nodeoffset) |
|||
{ |
|||
int nodedepth; |
|||
int err; |
|||
|
|||
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); |
|||
if (err) |
|||
return (err < 0) ? err : -FDT_ERR_INTERNAL; |
|||
return nodedepth; |
|||
} |
|||
|
|||
int fdt_parent_offset(const void *fdt, int nodeoffset) |
|||
{ |
|||
int nodedepth = fdt_node_depth(fdt, nodeoffset); |
|||
|
|||
if (nodedepth < 0) |
|||
return nodedepth; |
|||
return fdt_supernode_atdepth_offset(fdt, nodeoffset, |
|||
nodedepth - 1, NULL); |
|||
} |
|||
|
|||
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, |
|||
const char *propname, |
|||
const void *propval, int proplen) |
|||
{ |
|||
int offset; |
|||
const void *val; |
|||
int len; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
|||
* property of a node in fdt_getprop(), then if that didn't |
|||
* find what we want, we scan over them again making our way |
|||
* to the next node. Still it's the easiest to implement |
|||
* approach; performance can come later. */ |
|||
for (offset = fdt_next_node(fdt, startoffset, NULL); |
|||
offset >= 0; |
|||
offset = fdt_next_node(fdt, offset, NULL)) { |
|||
val = fdt_getprop(fdt, offset, propname, &len); |
|||
if (val && (len == proplen) |
|||
&& (memcmp(val, propval, len) == 0)) |
|||
return offset; |
|||
} |
|||
|
|||
return offset; /* error from fdt_next_node() */ |
|||
} |
|||
|
|||
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) |
|||
{ |
|||
int offset; |
|||
|
|||
if ((phandle == 0) || (phandle == -1)) |
|||
return -FDT_ERR_BADPHANDLE; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
/* FIXME: The algorithm here is pretty horrible: we
|
|||
* potentially scan each property of a node in |
|||
* fdt_get_phandle(), then if that didn't find what |
|||
* we want, we scan over them again making our way to the next |
|||
* node. Still it's the easiest to implement approach; |
|||
* performance can come later. */ |
|||
for (offset = fdt_next_node(fdt, -1, NULL); |
|||
offset >= 0; |
|||
offset = fdt_next_node(fdt, offset, NULL)) { |
|||
if (fdt_get_phandle(fdt, offset) == phandle) |
|||
return offset; |
|||
} |
|||
|
|||
return offset; /* error from fdt_next_node() */ |
|||
} |
|||
|
|||
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) |
|||
{ |
|||
int len = strlen(str); |
|||
const char *p; |
|||
|
|||
while (listlen >= len) { |
|||
if (memcmp(str, strlist, len+1) == 0) |
|||
return 1; |
|||
p = memchr(strlist, '\0', listlen); |
|||
if (!p) |
|||
return 0; /* malformed strlist.. */ |
|||
listlen -= (p-strlist) + 1; |
|||
strlist = p + 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) |
|||
{ |
|||
const char *list, *end; |
|||
int length, count = 0; |
|||
|
|||
list = fdt_getprop(fdt, nodeoffset, property, &length); |
|||
if (!list) |
|||
return length; |
|||
|
|||
end = list + length; |
|||
|
|||
while (list < end) { |
|||
length = strnlen(list, end - list) + 1; |
|||
|
|||
/* Abort if the last string isn't properly NUL-terminated. */ |
|||
if (list + length > end) |
|||
return -FDT_ERR_BADVALUE; |
|||
|
|||
list += length; |
|||
count++; |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, |
|||
const char *string) |
|||
{ |
|||
int length, len, idx = 0; |
|||
const char *list, *end; |
|||
|
|||
list = fdt_getprop(fdt, nodeoffset, property, &length); |
|||
if (!list) |
|||
return length; |
|||
|
|||
len = strlen(string) + 1; |
|||
end = list + length; |
|||
|
|||
while (list < end) { |
|||
length = strnlen(list, end - list) + 1; |
|||
|
|||
/* Abort if the last string isn't properly NUL-terminated. */ |
|||
if (list + length > end) |
|||
return -FDT_ERR_BADVALUE; |
|||
|
|||
if (length == len && memcmp(list, string, length) == 0) |
|||
return idx; |
|||
|
|||
list += length; |
|||
idx++; |
|||
} |
|||
|
|||
return -FDT_ERR_NOTFOUND; |
|||
} |
|||
|
|||
const char *fdt_stringlist_get(const void *fdt, int nodeoffset, |
|||
const char *property, int idx, |
|||
int *lenp) |
|||
{ |
|||
const char *list, *end; |
|||
int length; |
|||
|
|||
list = fdt_getprop(fdt, nodeoffset, property, &length); |
|||
if (!list) { |
|||
if (lenp) |
|||
*lenp = length; |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
end = list + length; |
|||
|
|||
while (list < end) { |
|||
length = strnlen(list, end - list) + 1; |
|||
|
|||
/* Abort if the last string isn't properly NUL-terminated. */ |
|||
if (list + length > end) { |
|||
if (lenp) |
|||
*lenp = -FDT_ERR_BADVALUE; |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
if (idx == 0) { |
|||
if (lenp) |
|||
*lenp = length - 1; |
|||
|
|||
return list; |
|||
} |
|||
|
|||
list += length; |
|||
idx--; |
|||
} |
|||
|
|||
if (lenp) |
|||
*lenp = -FDT_ERR_NOTFOUND; |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
int fdt_node_check_compatible(const void *fdt, int nodeoffset, |
|||
const char *compatible) |
|||
{ |
|||
const void *prop; |
|||
int len; |
|||
|
|||
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); |
|||
if (!prop) |
|||
return len; |
|||
|
|||
return !fdt_stringlist_contains(prop, len, compatible); |
|||
} |
|||
|
|||
int fdt_node_offset_by_compatible(const void *fdt, int startoffset, |
|||
const char *compatible) |
|||
{ |
|||
int offset, err; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
|||
* property of a node in fdt_node_check_compatible(), then if |
|||
* that didn't find what we want, we scan over them again |
|||
* making our way to the next node. Still it's the easiest to |
|||
* implement approach; performance can come later. */ |
|||
for (offset = fdt_next_node(fdt, startoffset, NULL); |
|||
offset >= 0; |
|||
offset = fdt_next_node(fdt, offset, NULL)) { |
|||
err = fdt_node_check_compatible(fdt, offset, compatible); |
|||
if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) |
|||
return err; |
|||
else if (err == 0) |
|||
return offset; |
|||
} |
|||
|
|||
return offset; /* error from fdt_next_node() */ |
|||
} |
|||
|
|||
int fdt_check_full(const void *fdt, size_t bufsize) |
|||
{ |
|||
int err; |
|||
int num_memrsv; |
|||
int offset, nextoffset = 0; |
|||
uint32_t tag; |
|||
unsigned depth = 0; |
|||
const void *prop; |
|||
const char *propname; |
|||
|
|||
if (bufsize < FDT_V1_SIZE) |
|||
return -FDT_ERR_TRUNCATED; |
|||
err = fdt_check_header(fdt); |
|||
if (err != 0) |
|||
return err; |
|||
if (bufsize < fdt_totalsize(fdt)) |
|||
return -FDT_ERR_TRUNCATED; |
|||
|
|||
num_memrsv = fdt_num_mem_rsv(fdt); |
|||
if (num_memrsv < 0) |
|||
return num_memrsv; |
|||
|
|||
while (1) { |
|||
offset = nextoffset; |
|||
tag = fdt_next_tag(fdt, offset, &nextoffset); |
|||
|
|||
if (nextoffset < 0) |
|||
return nextoffset; |
|||
|
|||
switch (tag) { |
|||
case FDT_NOP: |
|||
break; |
|||
|
|||
case FDT_END: |
|||
if (depth != 0) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
return 0; |
|||
|
|||
case FDT_BEGIN_NODE: |
|||
depth++; |
|||
if (depth > INT_MAX) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
break; |
|||
|
|||
case FDT_END_NODE: |
|||
if (depth == 0) |
|||
return -FDT_ERR_BADSTRUCTURE; |
|||
depth--; |
|||
break; |
|||
|
|||
case FDT_PROP: |
|||
prop = fdt_getprop_by_offset(fdt, offset, &propname, |
|||
&err); |
|||
if (!prop) |
|||
return err; |
|||
break; |
|||
|
|||
default: |
|||
return -FDT_ERR_INTERNAL; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,505 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
static int fdt_blocks_misordered_(const void *fdt, |
|||
int mem_rsv_size, int struct_size) |
|||
{ |
|||
return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) |
|||
|| (fdt_off_dt_struct(fdt) < |
|||
(fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) |
|||
|| (fdt_off_dt_strings(fdt) < |
|||
(fdt_off_dt_struct(fdt) + struct_size)) |
|||
|| (fdt_totalsize(fdt) < |
|||
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); |
|||
} |
|||
|
|||
static int fdt_rw_probe_(void *fdt) |
|||
{ |
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
if (fdt_version(fdt) < 17) |
|||
return -FDT_ERR_BADVERSION; |
|||
if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), |
|||
fdt_size_dt_struct(fdt))) |
|||
return -FDT_ERR_BADLAYOUT; |
|||
if (fdt_version(fdt) > 17) |
|||
fdt_set_version(fdt, 17); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#define FDT_RW_PROBE(fdt) \ |
|||
{ \ |
|||
int err_; \ |
|||
if ((err_ = fdt_rw_probe_(fdt)) != 0) \ |
|||
return err_; \ |
|||
} |
|||
|
|||
static inline int fdt_data_size_(void *fdt) |
|||
{ |
|||
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); |
|||
} |
|||
|
|||
static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) |
|||
{ |
|||
char *p = splicepoint; |
|||
char *end = (char *)fdt + fdt_data_size_(fdt); |
|||
|
|||
if (((p + oldlen) < p) || ((p + oldlen) > end)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) |
|||
return -FDT_ERR_BADOFFSET; |
|||
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) |
|||
return -FDT_ERR_NOSPACE; |
|||
memmove(p + newlen, p + oldlen, end - p - oldlen); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, |
|||
int oldn, int newn) |
|||
{ |
|||
int delta = (newn - oldn) * sizeof(*p); |
|||
int err; |
|||
err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); |
|||
if (err) |
|||
return err; |
|||
fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); |
|||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_splice_struct_(void *fdt, void *p, |
|||
int oldlen, int newlen) |
|||
{ |
|||
int delta = newlen - oldlen; |
|||
int err; |
|||
|
|||
if ((err = fdt_splice_(fdt, p, oldlen, newlen))) |
|||
return err; |
|||
|
|||
fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); |
|||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_splice_string_(void *fdt, int newlen) |
|||
{ |
|||
void *p = (char *)fdt |
|||
+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); |
|||
int err; |
|||
|
|||
if ((err = fdt_splice_(fdt, p, 0, newlen))) |
|||
return err; |
|||
|
|||
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_find_add_string_(void *fdt, const char *s) |
|||
{ |
|||
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); |
|||
const char *p; |
|||
char *new; |
|||
int len = strlen(s) + 1; |
|||
int err; |
|||
|
|||
p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); |
|||
if (p) |
|||
/* found it */ |
|||
return (p - strtab); |
|||
|
|||
new = strtab + fdt_size_dt_strings(fdt); |
|||
err = fdt_splice_string_(fdt, len); |
|||
if (err) |
|||
return err; |
|||
|
|||
memcpy(new, s, len); |
|||
return (new - strtab); |
|||
} |
|||
|
|||
int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) |
|||
{ |
|||
struct fdt_reserve_entry *re; |
|||
int err; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); |
|||
err = fdt_splice_mem_rsv_(fdt, re, 0, 1); |
|||
if (err) |
|||
return err; |
|||
|
|||
re->address = cpu_to_fdt64(address); |
|||
re->size = cpu_to_fdt64(size); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_del_mem_rsv(void *fdt, int n) |
|||
{ |
|||
struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
if (n >= fdt_num_mem_rsv(fdt)) |
|||
return -FDT_ERR_NOTFOUND; |
|||
|
|||
return fdt_splice_mem_rsv_(fdt, re, 1, 0); |
|||
} |
|||
|
|||
static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, |
|||
int len, struct fdt_property **prop) |
|||
{ |
|||
int oldlen; |
|||
int err; |
|||
|
|||
*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); |
|||
if (!*prop) |
|||
return oldlen; |
|||
|
|||
if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), |
|||
FDT_TAGALIGN(len)))) |
|||
return err; |
|||
|
|||
(*prop)->len = cpu_to_fdt32(len); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, |
|||
int len, struct fdt_property **prop) |
|||
{ |
|||
int proplen; |
|||
int nextoffset; |
|||
int namestroff; |
|||
int err; |
|||
|
|||
if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) |
|||
return nextoffset; |
|||
|
|||
namestroff = fdt_find_add_string_(fdt, name); |
|||
if (namestroff < 0) |
|||
return namestroff; |
|||
|
|||
*prop = fdt_offset_ptr_w_(fdt, nextoffset); |
|||
proplen = sizeof(**prop) + FDT_TAGALIGN(len); |
|||
|
|||
err = fdt_splice_struct_(fdt, *prop, 0, proplen); |
|||
if (err) |
|||
return err; |
|||
|
|||
(*prop)->tag = cpu_to_fdt32(FDT_PROP); |
|||
(*prop)->nameoff = cpu_to_fdt32(namestroff); |
|||
(*prop)->len = cpu_to_fdt32(len); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_set_name(void *fdt, int nodeoffset, const char *name) |
|||
{ |
|||
char *namep; |
|||
int oldlen, newlen; |
|||
int err; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); |
|||
if (!namep) |
|||
return oldlen; |
|||
|
|||
newlen = strlen(name); |
|||
|
|||
err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), |
|||
FDT_TAGALIGN(newlen+1)); |
|||
if (err) |
|||
return err; |
|||
|
|||
memcpy(namep, name, newlen+1); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, |
|||
int len, void **prop_data) |
|||
{ |
|||
struct fdt_property *prop; |
|||
int err; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); |
|||
if (err == -FDT_ERR_NOTFOUND) |
|||
err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); |
|||
if (err) |
|||
return err; |
|||
|
|||
*prop_data = prop->data; |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_setprop(void *fdt, int nodeoffset, const char *name, |
|||
const void *val, int len) |
|||
{ |
|||
void *prop_data; |
|||
int err; |
|||
|
|||
err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); |
|||
if (err) |
|||
return err; |
|||
|
|||
if (len) |
|||
memcpy(prop_data, val, len); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_appendprop(void *fdt, int nodeoffset, const char *name, |
|||
const void *val, int len) |
|||
{ |
|||
struct fdt_property *prop; |
|||
int err, oldlen, newlen; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); |
|||
if (prop) { |
|||
newlen = len + oldlen; |
|||
err = fdt_splice_struct_(fdt, prop->data, |
|||
FDT_TAGALIGN(oldlen), |
|||
FDT_TAGALIGN(newlen)); |
|||
if (err) |
|||
return err; |
|||
prop->len = cpu_to_fdt32(newlen); |
|||
memcpy(prop->data + oldlen, val, len); |
|||
} else { |
|||
err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); |
|||
if (err) |
|||
return err; |
|||
memcpy(prop->data, val, len); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_delprop(void *fdt, int nodeoffset, const char *name) |
|||
{ |
|||
struct fdt_property *prop; |
|||
int len, proplen; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len); |
|||
if (!prop) |
|||
return len; |
|||
|
|||
proplen = sizeof(*prop) + FDT_TAGALIGN(len); |
|||
return fdt_splice_struct_(fdt, prop, proplen, 0); |
|||
} |
|||
|
|||
int fdt_add_subnode_namelen(void *fdt, int parentoffset, |
|||
const char *name, int namelen) |
|||
{ |
|||
struct fdt_node_header *nh; |
|||
int offset, nextoffset; |
|||
int nodelen; |
|||
int err; |
|||
uint32_t tag; |
|||
fdt32_t *endtag; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); |
|||
if (offset >= 0) |
|||
return -FDT_ERR_EXISTS; |
|||
else if (offset != -FDT_ERR_NOTFOUND) |
|||
return offset; |
|||
|
|||
/* Try to place the new node after the parent's properties */ |
|||
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ |
|||
do { |
|||
offset = nextoffset; |
|||
tag = fdt_next_tag(fdt, offset, &nextoffset); |
|||
} while ((tag == FDT_PROP) || (tag == FDT_NOP)); |
|||
|
|||
nh = fdt_offset_ptr_w_(fdt, offset); |
|||
nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; |
|||
|
|||
err = fdt_splice_struct_(fdt, nh, 0, nodelen); |
|||
if (err) |
|||
return err; |
|||
|
|||
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); |
|||
memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); |
|||
memcpy(nh->name, name, namelen); |
|||
endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); |
|||
*endtag = cpu_to_fdt32(FDT_END_NODE); |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_add_subnode(void *fdt, int parentoffset, const char *name) |
|||
{ |
|||
return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); |
|||
} |
|||
|
|||
int fdt_del_node(void *fdt, int nodeoffset) |
|||
{ |
|||
int endoffset; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
endoffset = fdt_node_end_offset_(fdt, nodeoffset); |
|||
if (endoffset < 0) |
|||
return endoffset; |
|||
|
|||
return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), |
|||
endoffset - nodeoffset, 0); |
|||
} |
|||
|
|||
static void fdt_packblocks_(const char *old, char *new, |
|||
int mem_rsv_size, int struct_size) |
|||
{ |
|||
int mem_rsv_off, struct_off, strings_off; |
|||
|
|||
mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); |
|||
struct_off = mem_rsv_off + mem_rsv_size; |
|||
strings_off = struct_off + struct_size; |
|||
|
|||
memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); |
|||
fdt_set_off_mem_rsvmap(new, mem_rsv_off); |
|||
|
|||
memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); |
|||
fdt_set_off_dt_struct(new, struct_off); |
|||
fdt_set_size_dt_struct(new, struct_size); |
|||
|
|||
memmove(new + strings_off, old + fdt_off_dt_strings(old), |
|||
fdt_size_dt_strings(old)); |
|||
fdt_set_off_dt_strings(new, strings_off); |
|||
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); |
|||
} |
|||
|
|||
int fdt_open_into(const void *fdt, void *buf, int bufsize) |
|||
{ |
|||
int err; |
|||
int mem_rsv_size, struct_size; |
|||
int newsize; |
|||
const char *fdtstart = fdt; |
|||
const char *fdtend = fdtstart + fdt_totalsize(fdt); |
|||
char *tmp; |
|||
|
|||
FDT_RO_PROBE(fdt); |
|||
|
|||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) |
|||
* sizeof(struct fdt_reserve_entry); |
|||
|
|||
if (fdt_version(fdt) >= 17) { |
|||
struct_size = fdt_size_dt_struct(fdt); |
|||
} else { |
|||
struct_size = 0; |
|||
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) |
|||
; |
|||
if (struct_size < 0) |
|||
return struct_size; |
|||
} |
|||
|
|||
if (!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { |
|||
/* no further work necessary */ |
|||
err = fdt_move(fdt, buf, bufsize); |
|||
if (err) |
|||
return err; |
|||
fdt_set_version(buf, 17); |
|||
fdt_set_size_dt_struct(buf, struct_size); |
|||
fdt_set_totalsize(buf, bufsize); |
|||
return 0; |
|||
} |
|||
|
|||
/* Need to reorder */ |
|||
newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size |
|||
+ struct_size + fdt_size_dt_strings(fdt); |
|||
|
|||
if (bufsize < newsize) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
/* First attempt to build converted tree at beginning of buffer */ |
|||
tmp = buf; |
|||
/* But if that overlaps with the old tree... */ |
|||
if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { |
|||
/* Try right after the old tree instead */ |
|||
tmp = (char *)(uintptr_t)fdtend; |
|||
if ((tmp + newsize) > ((char *)buf + bufsize)) |
|||
return -FDT_ERR_NOSPACE; |
|||
} |
|||
|
|||
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); |
|||
memmove(buf, tmp, newsize); |
|||
|
|||
fdt_set_magic(buf, FDT_MAGIC); |
|||
fdt_set_totalsize(buf, bufsize); |
|||
fdt_set_version(buf, 17); |
|||
fdt_set_last_comp_version(buf, 16); |
|||
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int fdt_pack(void *fdt) |
|||
{ |
|||
int mem_rsv_size; |
|||
|
|||
FDT_RW_PROBE(fdt); |
|||
|
|||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) |
|||
* sizeof(struct fdt_reserve_entry); |
|||
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); |
|||
fdt_set_totalsize(fdt, fdt_data_size_(fdt)); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,102 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
struct fdt_errtabent { |
|||
const char *str; |
|||
}; |
|||
|
|||
#define FDT_ERRTABENT(val) \ |
|||
[(val)] = { .str = #val, } |
|||
|
|||
static struct fdt_errtabent fdt_errtable[] = { |
|||
FDT_ERRTABENT(FDT_ERR_NOTFOUND), |
|||
FDT_ERRTABENT(FDT_ERR_EXISTS), |
|||
FDT_ERRTABENT(FDT_ERR_NOSPACE), |
|||
|
|||
FDT_ERRTABENT(FDT_ERR_BADOFFSET), |
|||
FDT_ERRTABENT(FDT_ERR_BADPATH), |
|||
FDT_ERRTABENT(FDT_ERR_BADPHANDLE), |
|||
FDT_ERRTABENT(FDT_ERR_BADSTATE), |
|||
|
|||
FDT_ERRTABENT(FDT_ERR_TRUNCATED), |
|||
FDT_ERRTABENT(FDT_ERR_BADMAGIC), |
|||
FDT_ERRTABENT(FDT_ERR_BADVERSION), |
|||
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), |
|||
FDT_ERRTABENT(FDT_ERR_BADLAYOUT), |
|||
FDT_ERRTABENT(FDT_ERR_INTERNAL), |
|||
FDT_ERRTABENT(FDT_ERR_BADNCELLS), |
|||
FDT_ERRTABENT(FDT_ERR_BADVALUE), |
|||
FDT_ERRTABENT(FDT_ERR_BADOVERLAY), |
|||
FDT_ERRTABENT(FDT_ERR_NOPHANDLES), |
|||
}; |
|||
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) |
|||
|
|||
const char *fdt_strerror(int errval) |
|||
{ |
|||
if (errval > 0) |
|||
return "<valid offset/length>"; |
|||
else if (errval == 0) |
|||
return "<no error>"; |
|||
else if (errval > -FDT_ERRTABSIZE) { |
|||
const char *s = fdt_errtable[-errval].str; |
|||
|
|||
if (s) |
|||
return s; |
|||
} |
|||
|
|||
return "<unknown error>"; |
|||
} |
@ -0,0 +1,365 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
static int fdt_sw_probe_(void *fdt) |
|||
{ |
|||
if (fdt_magic(fdt) == FDT_MAGIC) |
|||
return -FDT_ERR_BADSTATE; |
|||
else if (fdt_magic(fdt) != FDT_SW_MAGIC) |
|||
return -FDT_ERR_BADMAGIC; |
|||
return 0; |
|||
} |
|||
|
|||
#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: |
|||
* fdt_add_reservmap_entry() |
|||
* 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; |
|||
|
|||
if (fdt_off_dt_strings(fdt) != 0) |
|||
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; |
|||
|
|||
if (fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) |
|||
return -FDT_ERR_BADSTATE; |
|||
return 0; |
|||
} |
|||
|
|||
#define FDT_SW_PROBE_STRUCT(fdt) \ |
|||
{ \ |
|||
int err; \ |
|||
if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ |
|||
return err; \ |
|||
} |
|||
|
|||
/* 'complete' state: Enter this state after fdt_finish()
|
|||
* |
|||
* Allowed functions: none |
|||
*/ |
|||
|
|||
static void *fdt_grab_space_(void *fdt, size_t len) |
|||
{ |
|||
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); |
|||
} |
|||
|
|||
int fdt_create(void *buf, int bufsize) |
|||
{ |
|||
const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header), |
|||
sizeof(struct fdt_reserve_entry)); |
|||
void *fdt = buf; |
|||
|
|||
if (bufsize < hdrsize) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
memset(buf, 0, bufsize); |
|||
|
|||
fdt_set_magic(fdt, FDT_SW_MAGIC); |
|||
fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); |
|||
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); |
|||
fdt_set_totalsize(fdt, bufsize); |
|||
|
|||
fdt_set_off_mem_rsvmap(fdt, hdrsize); |
|||
fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); |
|||
fdt_set_off_dt_strings(fdt, 0); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int fdt_resize(void *fdt, void *buf, int bufsize) |
|||
{ |
|||
size_t headsize, tailsize; |
|||
char *oldtail, *newtail; |
|||
|
|||
FDT_SW_PROBE(fdt); |
|||
|
|||
headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); |
|||
tailsize = fdt_size_dt_strings(fdt); |
|||
|
|||
if ((headsize + tailsize) > fdt_totalsize(fdt)) |
|||
return -FDT_ERR_INTERNAL; |
|||
|
|||
if ((headsize + tailsize) > bufsize) |
|||
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); |
|||
if (fdt_off_dt_strings(buf)) |
|||
fdt_set_off_dt_strings(buf, bufsize); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) |
|||
{ |
|||
struct fdt_reserve_entry *re; |
|||
int offset; |
|||
|
|||
FDT_SW_PROBE_MEMRSV(fdt); |
|||
|
|||
offset = fdt_off_dt_struct(fdt); |
|||
if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
re = (struct fdt_reserve_entry *)((char *)fdt + offset); |
|||
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) |
|||
{ |
|||
int err = fdt_add_reservemap_entry(fdt, 0, 0); |
|||
|
|||
if (err) |
|||
return err; |
|||
|
|||
fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_begin_node(void *fdt, const char *name) |
|||
{ |
|||
struct fdt_node_header *nh; |
|||
int namelen; |
|||
|
|||
FDT_SW_PROBE_STRUCT(fdt); |
|||
|
|||
namelen = strlen(name) + 1; |
|||
nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); |
|||
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) |
|||
{ |
|||
fdt32_t *en; |
|||
|
|||
FDT_SW_PROBE_STRUCT(fdt); |
|||
|
|||
en = fdt_grab_space_(fdt, FDT_TAGSIZE); |
|||
if (! en) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
*en = cpu_to_fdt32(FDT_END_NODE); |
|||
return 0; |
|||
} |
|||
|
|||
static int fdt_find_add_string_(void *fdt, const char *s) |
|||
{ |
|||
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_placeholder(void *fdt, const char *name, int len, void **valp) |
|||
{ |
|||
struct fdt_property *prop; |
|||
int nameoff; |
|||
|
|||
FDT_SW_PROBE_STRUCT(fdt); |
|||
|
|||
nameoff = fdt_find_add_string_(fdt, name); |
|||
if (nameoff == 0) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); |
|||
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); |
|||
*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); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_finish(void *fdt) |
|||
{ |
|||
char *p = (char *)fdt; |
|||
fdt32_t *end; |
|||
int oldstroffset, newstroffset; |
|||
uint32_t tag; |
|||
int offset, nextoffset; |
|||
|
|||
FDT_SW_PROBE_STRUCT(fdt); |
|||
|
|||
/* Add terminator */ |
|||
end = fdt_grab_space_(fdt, sizeof(*end)); |
|||
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); |
|||
int nameoff; |
|||
|
|||
nameoff = fdt32_to_cpu(prop->nameoff); |
|||
nameoff += fdt_size_dt_strings(fdt); |
|||
prop->nameoff = cpu_to_fdt32(nameoff); |
|||
} |
|||
offset = nextoffset; |
|||
} |
|||
if (nextoffset < 0) |
|||
return nextoffset; |
|||
|
|||
/* Finally, adjust the header */ |
|||
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); |
|||
fdt_set_magic(fdt, FDT_MAGIC); |
|||
return 0; |
|||
} |
@ -0,0 +1,139 @@ |
|||
/*
|
|||
* 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" |
|||
|
|||
int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, |
|||
const char *name, int namelen, |
|||
uint32_t idx, const void *val, |
|||
int len) |
|||
{ |
|||
void *propval; |
|||
int proplen; |
|||
|
|||
propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, |
|||
&proplen); |
|||
if (!propval) |
|||
return proplen; |
|||
|
|||
if (proplen < (len + idx)) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
memcpy((char *)propval + idx, val, len); |
|||
return 0; |
|||
} |
|||
|
|||
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, |
|||
const void *val, int len) |
|||
{ |
|||
const void *propval; |
|||
int proplen; |
|||
|
|||
propval = fdt_getprop(fdt, nodeoffset, name, &proplen); |
|||
if (!propval) |
|||
return proplen; |
|||
|
|||
if (proplen != len) |
|||
return -FDT_ERR_NOSPACE; |
|||
|
|||
return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, |
|||
strlen(name), 0, |
|||
val, len); |
|||
} |
|||
|
|||
static void fdt_nop_region_(void *start, int len) |
|||
{ |
|||
fdt32_t *p; |
|||
|
|||
for (p = start; (char *)p < ((char *)start + len); p++) |
|||
*p = cpu_to_fdt32(FDT_NOP); |
|||
} |
|||
|
|||
int fdt_nop_property(void *fdt, int nodeoffset, const char *name) |
|||
{ |
|||
struct fdt_property *prop; |
|||
int len; |
|||
|
|||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len); |
|||
if (!prop) |
|||
return len; |
|||
|
|||
fdt_nop_region_(prop, len + sizeof(*prop)); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int fdt_node_end_offset_(void *fdt, int offset) |
|||
{ |
|||
int depth = 0; |
|||
|
|||
while ((offset >= 0) && (depth >= 0)) |
|||
offset = fdt_next_node(fdt, offset, &depth); |
|||
|
|||
return offset; |
|||
} |
|||
|
|||
int fdt_nop_node(void *fdt, int nodeoffset) |
|||
{ |
|||
int endoffset; |
|||
|
|||
endoffset = fdt_node_end_offset_(fdt, nodeoffset); |
|||
if (endoffset < 0) |
|||
return endoffset; |
|||
|
|||
fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), |
|||
endoffset - nodeoffset); |
|||
return 0; |
|||
} |
@ -0,0 +1,96 @@ |
|||
#ifndef LIBFDT_INTERNAL_H |
|||
#define LIBFDT_INTERNAL_H |
|||
/*
|
|||
* 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 <fdt.h> |
|||
|
|||
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) |
|||
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) |
|||
|
|||
int fdt_ro_probe_(const void *fdt); |
|||
#define FDT_RO_PROBE(fdt) \ |
|||
{ \ |
|||
int err_; \ |
|||
if ((err_ = fdt_ro_probe_(fdt)) != 0) \ |
|||
return err_; \ |
|||
} |
|||
|
|||
int fdt_check_node_offset_(const void *fdt, int offset); |
|||
int fdt_check_prop_offset_(const void *fdt, int offset); |
|||
const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); |
|||
int fdt_node_end_offset_(void *fdt, int nodeoffset); |
|||
|
|||
static inline const void *fdt_offset_ptr_(const void *fdt, int offset) |
|||
{ |
|||
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; |
|||
} |
|||
|
|||
static inline void *fdt_offset_ptr_w_(void *fdt, int offset) |
|||
{ |
|||
return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); |
|||
} |
|||
|
|||
static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) |
|||
{ |
|||
const struct fdt_reserve_entry *rsv_table = |
|||
(const struct fdt_reserve_entry *) |
|||
((const char *)fdt + fdt_off_mem_rsvmap(fdt)); |
|||
|
|||
return rsv_table + n; |
|||
} |
|||
static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) |
|||
{ |
|||
return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); |
|||
} |
|||
|
|||
#define FDT_SW_MAGIC (~FDT_MAGIC) |
|||
|
|||
#endif /* LIBFDT_INTERNAL_H */ |
Loading…
Reference in new issue