mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

This adds the following commits from upstream: 87963ee20693 livetree: add missing type markers in generated overlay properties 825146d13dc0 Fix typos in various documentation and source files 25bb080c18d1 Update the GPL2 text to the latest revision 243176c4ce84 Fix bogus error on rebuild ce01b21098a4 libfdt: Add FDT_CREATE_FLAG_NO_NAME_DEDUP flag that trades size for speed fbb62754ce45 libfdt: Introduce fdt_create_with_flags() 228a44cce857 libfdt: Ensure fdt_add_property frees allocated name string on failure 8f695676227b Avoid assertion in check_interrupts_property() 5c3513f68921 Link tools and tests against libfdt shared library 00f9febf9c16 tests: Rename tests.sh to testutils.sh c5d45188f923 Clean up LDLIBS handling 6ef8fcd05b74 Rebuild libfdt shared object if versioning linker script changes 26ee65a16c38 Use Python3 by default cca6546244cb libfdt: Make fdt_get_max_phandle() an inline 730875016a6a libfdt: Add phandle generation helper 7dfb61ba96b1 libfdt: Use fdt_find_max_phandle() 2bc5b66d7f6c libfdt: Add new maximum phandle lookup function 7fcf8208b8a9 libfdt: add fdt_append_addrrange() ae795b2db7a4 checks: Do not omit nodes with labels if symbol generation is requested eac2ad495b29 Update version.lds again f67b47135523 Revert "libfdt: Add phandle generation helper" 54ea41c22415 libfdt: Add phandle generation helper 4762ad051ee0 checks: Fix spelling in check_graph_endpoint d37f6b20107e Bump version to v1.5.0 a4b1a307ff3a pylibfdt:tests: Extend the way how to find a Python module 625dd8aaf20f pylibfdt: Change how passing tests are recognized 364631626bb7 pylibfdt: Test fdt.setprop take bytes on Python 3, add error handling cb0f454f73cc pylibfdt: check_err accepts only integer as a first argument. 4b68c6b3605a pylibfdt: Proper handling of bytes/unicode strings and octal literals 78e113e81c9d Use PRIxPTR for printing uintptr_t values ea7a8f6dad67 libfdt: Fix FDT_ERR_NOTFOUND typos in documentation 5aafd7ca43e0 libfdt: Fix fdt_getprop_by_offset() parameter name in documentation 7cbc550f903b checks: Add unit address check if node is enabled Signed-off-by: Rob Herring <robh@kernel.org>
421 lines
10 KiB
C
421 lines
10 KiB
C
/*
|
|
* 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; \
|
|
}
|
|
|
|
static inline uint32_t sw_flags(void *fdt)
|
|
{
|
|
/* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
|
|
return fdt_last_comp_version(fdt);
|
|
}
|
|
|
|
/* '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_with_flags(void *buf, int bufsize, uint32_t flags)
|
|
{
|
|
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;
|
|
|
|
if (flags & ~FDT_CREATE_FLAGS_ALL)
|
|
return -FDT_ERR_BADFLAGS;
|
|
|
|
memset(buf, 0, bufsize);
|
|
|
|
/*
|
|
* magic and last_comp_version keep intermediate state during the fdt
|
|
* creation process, which is replaced with the proper FDT format by
|
|
* fdt_finish().
|
|
*
|
|
* flags should be accessed with sw_flags().
|
|
*/
|
|
fdt_set_magic(fdt, FDT_SW_MAGIC);
|
|
fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
|
|
fdt_set_last_comp_version(fdt, flags);
|
|
|
|
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_create(void *buf, int bufsize)
|
|
{
|
|
return fdt_create_with_flags(buf, bufsize, 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_add_string_(void *fdt, const char *s)
|
|
{
|
|
char *strtab = (char *)fdt + fdt_totalsize(fdt);
|
|
int strtabsize = fdt_size_dt_strings(fdt);
|
|
int len = strlen(s) + 1;
|
|
int struct_top, offset;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Must only be used to roll back in case of error */
|
|
static void fdt_del_last_string_(void *fdt, const char *s)
|
|
{
|
|
int strtabsize = fdt_size_dt_strings(fdt);
|
|
int len = strlen(s) + 1;
|
|
|
|
fdt_set_size_dt_strings(fdt, strtabsize - len);
|
|
}
|
|
|
|
static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
|
|
{
|
|
char *strtab = (char *)fdt + fdt_totalsize(fdt);
|
|
int strtabsize = fdt_size_dt_strings(fdt);
|
|
const char *p;
|
|
|
|
*allocated = 0;
|
|
|
|
p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
|
|
if (p)
|
|
return p - strtab;
|
|
|
|
*allocated = 1;
|
|
|
|
return fdt_add_string_(fdt, s);
|
|
}
|
|
|
|
int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
|
|
{
|
|
struct fdt_property *prop;
|
|
int nameoff;
|
|
int allocated;
|
|
|
|
FDT_SW_PROBE_STRUCT(fdt);
|
|
|
|
/* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
|
|
if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
|
|
allocated = 1;
|
|
nameoff = fdt_add_string_(fdt, name);
|
|
} else {
|
|
nameoff = fdt_find_add_string_(fdt, name, &allocated);
|
|
}
|
|
if (nameoff == 0)
|
|
return -FDT_ERR_NOSPACE;
|
|
|
|
prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
|
|
if (! prop) {
|
|
if (allocated)
|
|
fdt_del_last_string_(fdt, name);
|
|
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));
|
|
|
|
/* And fix up fields that were keeping intermediate state. */
|
|
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
|
|
fdt_set_magic(fdt, FDT_MAGIC);
|
|
|
|
return 0;
|
|
}
|