f858927fd6
Major changes are I2C and SPI bus checks, YAML output format (for future validation), some new libfdt functions, and more libfdt validation of dtbs. The YAML addition adds an optional dependency on libyaml. pkg-config is used to test for it and pkg-config became a kconfig dependency in 4.18. This adds the following commits from upstream: c86da84d30e4 Add support for YAML encoded output 361b5e7d8067 Make type_marker_length helper public bfbfab047e45 pylibfdt: Add a means to add and delete notes 9005f4108e7c pylibfdt: Allow delprop() to return errors b94c056b137e Make valgrind optional fd06c54d4711 tests: Better testing of dtc -I fs mode c3f50c9a86d9 tests: Allow dtbs_equal_unordered to ignore mem reserves 0ac9fdee37c7 dtc: trivial '-I fs -O dts' test 0fd1c8c783f3 pylibfdt: fdt_get_mem_rsv returns 2 uint64_t values 04853cad18f4 pylibfdt: Don't incorrectly / unnecessarily override uint64_t typemap 9619c8619c37 Kill bogus TYPE_BLOB marker type ac68ff92ae20 parser: add TYPE_STRING marker to path references 90a190eb04d9 checks: add SPI bus checks 53a1bd546905 checks: add I2C bus checks 88f18909db73 dtc: Bump version to v1.4.7 85bce8b2f06d tests: Correction to vg_prepare_blob() 57f7f9e7bc7c tests: Don't call memcmp() with NULL arguments c12b2b0c20eb libfdt: fdt_address_cells() and fdt_size_cells() 3fe0eeda0b7f livetree: Set phandle properties type to uint32 853649acceba pylibfdt: Support the sequential-write interface 9b0e4fe26093 tests: Improve fdt_resize() tests 1087504bb3e8 libfdt: Add necessary header padding in fdt_create() c72fa777e613 libfdt: Copy the struct region in fdt_resize() 32b9c6130762 Preserve datatype markers when emitting dts format 6dcb8ba408ec libfdt: Add helpers for accessing unaligned words 42607f21d43e tests: Fix incorrect check name 'prop_name_chars' 9d78c33bf8a1 tests: fix grep for checks error messages b770f3d1c13f pylibfdt: Support setting the name of a node 2f0d07e678e0 pylibfdt: Add functions to set and get properties as strings 354d3dc55939 pylibfdt: Update the bytearray size with pack() 3c374d46acce pylibfdt: Allow reading integer values from properties 49d32ce40bb4 pylibfdt: Use an unsigned type for fdt32_t 481246a0c13a pylibfdt: Avoid accessing the internal _fdt member in tests 9aafa33d99ed pylibfdt: Add functions to update properties 5a598671fdbf pylibfdt: Support device-tree creation/expansion 483e170625e1 pylibfdt: Add support for reading the memory reserve map 29bb05aa4200 pylibfdt: Add support for the rest of the header functions 582a7159a5d0 pylibfdt: Add support for fdt_next_node() f0f8c9169819 pylibfdt: Reorder functions to match libfdt.h 64a69d123935 pylibfdt: Return string instead of bytearray from getprop() 4d09a83420df fdtput: Add documentation e617cbe1bd67 fdtget: Add documentation 180a93924014 Use <inttypes.h> format specifiers in a bunch of places we should b9af3b396576 scripts/dtc: Fixed format mismatch in fprintf 4b8fcc3d015c libfdt: Add fdt_check_full() function c14223fb2292 tests: Use valgrind client requests for better checking 5b67d2b955a3 tests: Better handling of valgrind errors saving blobs e2556aaeb506 tests: Remove unused #define fb9c6abddaa8 Use size_t for blob lengths in utilfdt_read* 0112fda03bf6 libfdt: Add fdt_header_size() 6473a21d8bfe Consolidate utilfdt_read_len() variants d5db5382c5e5 libfdt: Safer access to memory reservations 719d582e98ec libfdt: Propagate name errors in fdt_getprop_by_offset() 70166d62a27f libfdt: Safer access to strings section eb890c0f77dc libfdt: Make fdt_check_header() more thorough 899d6fad93f3 libfdt: Improve sequential write state checking 04b5b4062ccd libfdt: Clean up header checking functions 44d3efedc816 Preserve datatype information when parsing dts f0be81bd8de0 Make Property a subclass of bytearray 24b1f3f064d4 pylibfdt: Add a method to access the device tree directly Signed-off-by: Rob Herring <robh@kernel.org>
913 lines
24 KiB
C
913 lines
24 KiB
C
/*
|
|
* 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;
|
|
}
|