mirror of
https://github.com/dgibson/dtc.git
synced 2026-01-22 09:40:33 -05:00
Compare commits
No commits in common. "main" and "dwg-last" have entirely different histories.
340 changed files with 3530 additions and 32244 deletions
39
.cirrus.yml
39
.cirrus.yml
|
|
@ -1,39 +0,0 @@
|
|||
# FreeBSD build with multiple versions
|
||||
freebsd_versions_task:
|
||||
name: FreeBSD $FREEBSD_VERSION make build
|
||||
freebsd_instance:
|
||||
image_family: $FREEBSD_IMAGE
|
||||
matrix:
|
||||
- env:
|
||||
FREEBSD_VERSION: "13.5"
|
||||
FREEBSD_IMAGE: freebsd-13-5
|
||||
- env:
|
||||
FREEBSD_VERSION: "14.3"
|
||||
FREEBSD_IMAGE: freebsd-14-3
|
||||
install_script:
|
||||
- pkg install -y git gmake flex bison python3 py312-setuptools swig libyaml pkgconf
|
||||
build_script:
|
||||
- gmake
|
||||
check_script:
|
||||
- gmake check
|
||||
|
||||
# FreeBSD meson builds with multiple versions
|
||||
freebsd_meson_versions_task:
|
||||
name: FreeBSD $FREEBSD_VERSION meson build
|
||||
freebsd_instance:
|
||||
image_family: $FREEBSD_IMAGE
|
||||
matrix:
|
||||
- env:
|
||||
FREEBSD_VERSION: "13.5"
|
||||
FREEBSD_IMAGE: freebsd-13-5
|
||||
- env:
|
||||
FREEBSD_VERSION: "14.3"
|
||||
FREEBSD_IMAGE: freebsd-14-3
|
||||
install_script:
|
||||
- pkg install -y git meson ninja flex bison python3 py312-setuptools swig libyaml pkgconf
|
||||
setup_script:
|
||||
- meson setup -D python=enabled -D yaml=enabled build
|
||||
build_script:
|
||||
- meson compile -C build
|
||||
test_script:
|
||||
- if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi
|
||||
135
.clang-format
135
.clang-format
|
|
@ -1,135 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 11.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/dev-tools/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: false
|
||||
|
||||
# Taken from:
|
||||
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
|
||||
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | LC_ALL=C sort -u
|
||||
ForEachMacros:
|
||||
- 'fdt_for_each_property_offset'
|
||||
- 'fdt_for_each_subnode'
|
||||
- 'for_each_child'
|
||||
- 'for_each_child_withdel'
|
||||
- 'for_each_label'
|
||||
- 'for_each_label_withdel'
|
||||
- 'for_each_marker'
|
||||
- 'for_each_marker_of_type'
|
||||
- 'for_each_property'
|
||||
- 'for_each_property_withdel'
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentGotoLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# Taken from git's rules
|
||||
PenaltyBreakAssignment: 10
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatementsExceptForEachMacros
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
...
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# EditorConfig is a file format and collection of text editor plugins
|
||||
# for maintaining consistent coding styles between different editors
|
||||
# and IDEs. Most popular editors support this either natively or via
|
||||
# plugin.
|
||||
#
|
||||
# Check https://editorconfig.org for details.
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
|
||||
[Makefile*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = makefile
|
||||
|
||||
[*.[ch]]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[meson.build]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.lds]
|
||||
indent_style = tab
|
||||
114
.github/workflows/build.yml
vendored
114
.github/workflows/build.yml
vendored
|
|
@ -1,114 +0,0 @@
|
|||
---
|
||||
name: Build test
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- ci
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
|
||||
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-make:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ "alpine", "archlinux", "fedora", "ubuntu" ]
|
||||
|
||||
container:
|
||||
image: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
./scripts/install-deps.sh
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
make
|
||||
|
||||
- name: Run check
|
||||
run: |
|
||||
make check
|
||||
|
||||
build-meson:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ "alpine", "archlinux", "fedora", "ubuntu" ]
|
||||
|
||||
container:
|
||||
image: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
./scripts/install-deps.sh
|
||||
|
||||
- name: Setup
|
||||
run: meson setup -D python=enabled -D yaml=enabled build
|
||||
|
||||
- name: Build
|
||||
run: meson compile -C build
|
||||
|
||||
- name: Run check
|
||||
run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { sys: mingw32 }
|
||||
- { sys: mingw64 }
|
||||
- { sys: ucrt64 }
|
||||
- { sys: clang64 }
|
||||
name: ${{ matrix.sys }}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.sys}}
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
flex
|
||||
bison
|
||||
pacboy: >-
|
||||
toolchain:p
|
||||
meson:p
|
||||
ninja:p
|
||||
libyaml:p
|
||||
swig:p
|
||||
python-setuptools-scm:p
|
||||
|
||||
- name: '🚧 Build'
|
||||
run: |
|
||||
meson setup -Dtools=true -Dtests=false build
|
||||
meson compile -C build
|
||||
28
.gitignore
vendored
28
.gitignore
vendored
|
|
@ -1,30 +1,6 @@
|
|||
*.o
|
||||
*.d
|
||||
*.a
|
||||
*.patch
|
||||
*.so
|
||||
*.so.*
|
||||
*~
|
||||
*.bak
|
||||
*.tab.[ch]
|
||||
lex.yy.c
|
||||
*.lex.c
|
||||
.*.swp
|
||||
/dtc
|
||||
/fdtdump
|
||||
/convert-dtsv0
|
||||
/version_gen.h
|
||||
/fdtget
|
||||
/fdtput
|
||||
/fdtoverlay
|
||||
/patches
|
||||
/.pc
|
||||
|
||||
# cscope files
|
||||
cscope.*
|
||||
ncscope.*
|
||||
|
||||
.eggs/
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
dtc
|
||||
ftdump
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
stages:
|
||||
- build
|
||||
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"
|
||||
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "ci"
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
# Linux builds with make
|
||||
.build-make-template: &build-make-template
|
||||
stage: build
|
||||
before_script:
|
||||
- ./scripts/install-deps.sh
|
||||
script:
|
||||
- make
|
||||
- make check
|
||||
interruptible: true
|
||||
|
||||
build-make-alpine:
|
||||
<<: *build-make-template
|
||||
image: alpine:latest
|
||||
|
||||
build-make-archlinux:
|
||||
<<: *build-make-template
|
||||
image: archlinux:latest
|
||||
|
||||
build-make-fedora:
|
||||
<<: *build-make-template
|
||||
image: fedora:latest
|
||||
|
||||
build-make-ubuntu:
|
||||
<<: *build-make-template
|
||||
image: ubuntu:latest
|
||||
|
||||
# Linux builds with meson
|
||||
.build-meson-template: &build-meson-template
|
||||
stage: build
|
||||
before_script:
|
||||
- ./scripts/install-deps.sh
|
||||
script:
|
||||
- meson setup -D python=enabled -D yaml=enabled build
|
||||
- meson compile -C build
|
||||
- if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi
|
||||
interruptible: true
|
||||
|
||||
build-meson-alpine:
|
||||
<<: *build-meson-template
|
||||
image: alpine:latest
|
||||
|
||||
build-meson-archlinux:
|
||||
<<: *build-meson-template
|
||||
image: archlinux:latest
|
||||
|
||||
build-meson-fedora:
|
||||
<<: *build-meson-template
|
||||
image: fedora:latest
|
||||
|
||||
build-meson-ubuntu:
|
||||
<<: *build-meson-template
|
||||
image: ubuntu:latest
|
||||
|
||||
32
BSD-2-Clause
32
BSD-2-Clause
|
|
@ -1,32 +0,0 @@
|
|||
Valid-License-Identifier: BSD-2-Clause
|
||||
SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
|
||||
Usage-Guide:
|
||||
To use the BSD 2-clause "Simplified" License put the following SPDX
|
||||
tag/value pair into a comment according to the placement guidelines in
|
||||
the licensing rules documentation:
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
License-Text:
|
||||
|
||||
Copyright (c) <year> <owner> . All rights reserved.
|
||||
|
||||
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 HOLDER 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.
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
# Contributing to dtc or libfdt
|
||||
|
||||
There are two ways to submit changes for dtc or libfdt:
|
||||
|
||||
* Post patches directly to the
|
||||
[devicetree-compiler](mailto:devicetree-compiler@vger.kernel.org)
|
||||
mailing list.
|
||||
* Submit pull requests via
|
||||
[Github](https://github.com/dgibson/dtc/pulls)
|
||||
|
||||
## Adding a new function to libfdt.h
|
||||
|
||||
The shared library uses `libfdt/version.lds` to list the exported
|
||||
functions, so add your new function there. Check that your function
|
||||
works with pylibfdt. If it cannot be supported, put the declaration in
|
||||
`libfdt.h` behind `#ifndef SWIG` so that swig ignores it.
|
||||
|
||||
## Tests
|
||||
|
||||
Test files are kept in the `tests/` directory. Use `make check` to build and run
|
||||
all tests.
|
||||
|
||||
If you want to adjust a test file, be aware that `tree_tree1.dts` is compiled
|
||||
and checked against a binary tree from assembler macros in `trees.S`. So
|
||||
if you change that file you must change `tree.S` also.
|
||||
|
||||
## Developer's Certificate of Origin
|
||||
|
||||
Like many other projects, dtc and libfdt have adopted the "Developer's
|
||||
Certificate of Origin" (Signed-off-by) process created by the Linux
|
||||
kernel community to improve tracking of who did what. Here's how it
|
||||
works (this is a very slight modification of the description from
|
||||
`Documentation/process/submitting-patches.rst` in the kernel tree):
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the
|
||||
patch, which certifies that you wrote it or otherwise have the right
|
||||
to pass it on as an open-source patch. The rules are pretty simple:
|
||||
if you can certify the below:
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
|
||||
then you just add a line saying::
|
||||
|
||||
Signed-off-by: Random J Developer <random@developer.example.org>
|
||||
|
||||
using your real name (sorry, no pseudonyms or anonymous
|
||||
contributions.) This will be done for you automatically if you use
|
||||
`git commit -s`. Reverts should also include "Signed-off-by". `git
|
||||
revert -s` does that for you.
|
||||
|
||||
Any further SoBs (Signed-off-by:'s) following the author's SoB are
|
||||
from people handling and transporting the patch, but were not involved
|
||||
in its development. SoB chains should reflect the **real** route a
|
||||
patch took as it was propagated to the maintainers, with the first SoB
|
||||
entry signalling primary authorship of a single author.
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
|
|
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
|||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
|
|
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
|||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
|
|
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
|||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
|
|
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
|||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
|
|
@ -225,7 +225,7 @@ impose that choice.
|
|||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
|
|
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
|||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
|
|
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
|
|
@ -303,16 +303,17 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
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 program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
|
@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
|
|||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
1420
Documentation/booting-without-of.txt
Normal file
1420
Documentation/booting-without-of.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,310 +0,0 @@
|
|||
Device Tree Dynamic Object format internals
|
||||
-------------------------------------------
|
||||
|
||||
The Device Tree for most platforms is a static representation of
|
||||
the hardware capabilities. This is insufficient for platforms
|
||||
that need to dynamically insert Device Tree fragments into the
|
||||
live tree.
|
||||
|
||||
This document explains the Device Tree object format and
|
||||
modifications made to the Device Tree compiler, which make it possible.
|
||||
|
||||
1. Simplified Problem Definition
|
||||
--------------------------------
|
||||
|
||||
Assume we have a platform which boots using following simplified Device Tree.
|
||||
|
||||
---- foo.dts -----------------------------------------------------------------
|
||||
/* FOO platform */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
};
|
||||
};
|
||||
---- foo.dts -----------------------------------------------------------------
|
||||
|
||||
We have a number of peripherals that after probing (using some undefined method)
|
||||
should result in different Device Tree configuration.
|
||||
|
||||
We cannot boot with this static tree because due to the configuration of the
|
||||
foo platform there exist multiple conflicting peripherals DT fragments.
|
||||
|
||||
So for the bar peripheral we would have this:
|
||||
|
||||
---- foo+bar.dts -------------------------------------------------------------
|
||||
/* FOO platform + bar peripheral */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
|
||||
/* bar peripheral */
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
};
|
||||
};
|
||||
};
|
||||
---- foo+bar.dts -------------------------------------------------------------
|
||||
|
||||
While for the baz peripheral we would have this:
|
||||
|
||||
---- foo+baz.dts -------------------------------------------------------------
|
||||
/* FOO platform + baz peripheral */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
/* baz resources */
|
||||
baz_res: res_baz { ... };
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
|
||||
/* baz peripheral */
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
/* reference to another point in the tree */
|
||||
ref-to-res = <&baz_res>;
|
||||
... /* various properties and child nodes */
|
||||
};
|
||||
};
|
||||
};
|
||||
---- foo+baz.dts -------------------------------------------------------------
|
||||
|
||||
We note that the baz case is more complicated, since the baz peripheral needs to
|
||||
reference another node in the DT tree.
|
||||
|
||||
2. Device Tree Object Format Requirements
|
||||
-----------------------------------------
|
||||
|
||||
Since the Device Tree is used for booting a number of very different hardware
|
||||
platforms it is imperative that we tread very carefully.
|
||||
|
||||
2.a) No changes to the Device Tree binary format for the base tree. We cannot
|
||||
modify the tree format at all and all the information we require should be
|
||||
encoded using Device Tree itself. We can add nodes that can be safely ignored
|
||||
by both bootloaders and the kernel. The plugin dtbs are optionally tagged
|
||||
with a different magic number in the header but otherwise they're simple
|
||||
blobs.
|
||||
|
||||
2.b) Changes to the DTS source format should be absolutely minimal, and should
|
||||
only be needed for the DT fragment definitions, and not the base boot DT.
|
||||
|
||||
2.c) An explicit option should be used to instruct DTC to generate the required
|
||||
information needed for object resolution. Platforms that don't use the
|
||||
dynamic object format can safely ignore it.
|
||||
|
||||
2.d) Finally, DT syntax changes should be kept to a minimum. It should be
|
||||
possible to express everything using the existing DT syntax.
|
||||
|
||||
3. Implementation
|
||||
-----------------
|
||||
|
||||
The basic unit of addressing in Device Tree is the phandle. Turns out it's
|
||||
relatively simple to extend the way phandles are generated and referenced
|
||||
so that it's possible to dynamically convert symbolic references (labels)
|
||||
to phandle values. This is a valid assumption as long as the author uses
|
||||
reference syntax and does not assign phandle values manually (which might
|
||||
be a problem with decompiled source files).
|
||||
|
||||
We can roughly divide the operation into two steps.
|
||||
|
||||
3.a) Compilation of the base board DTS file using the '-@' option
|
||||
generates a valid DT blob with an added __symbols__ node at the root node,
|
||||
containing a list of all nodes that are marked with a label.
|
||||
|
||||
Using the foo.dts file above the following node will be generated;
|
||||
|
||||
$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
|
||||
$ fdtdump foo.dtb
|
||||
...
|
||||
/ {
|
||||
...
|
||||
res {
|
||||
...
|
||||
phandle = <0x00000001>;
|
||||
...
|
||||
};
|
||||
ocp {
|
||||
...
|
||||
phandle = <0x00000002>;
|
||||
...
|
||||
};
|
||||
__symbols__ {
|
||||
res="/res";
|
||||
ocp="/ocp";
|
||||
};
|
||||
};
|
||||
|
||||
Notice that all the nodes that had a label have been recorded, and that
|
||||
phandles have been generated for them.
|
||||
|
||||
This blob can be used to boot the board normally, the __symbols__ node will
|
||||
be safely ignored both by the bootloader and the kernel (the only loss will
|
||||
be a few bytes of memory and disk space).
|
||||
|
||||
We generate a __symbols__ node to record nodes that had labels in the base
|
||||
tree (or subsequent loaded overlays) so that they can be matched up with
|
||||
references made to them in Device Tree objects.
|
||||
|
||||
3.b) The Device Tree fragments must be compiled with the same option but they
|
||||
must also have a tag (/plugin/) that allows undefined references to nodes
|
||||
that are not present at compilation time to be recorded so that the runtime
|
||||
loader can fix them.
|
||||
|
||||
So the bar peripheral's DTS format would be of the form:
|
||||
|
||||
/dts-v1/;
|
||||
/plugin/; /* allow undefined references and record them */
|
||||
/ {
|
||||
.... /* various properties for loader use; i.e. part id etc. */
|
||||
fragment@0 {
|
||||
target = <&ocp>;
|
||||
__overlay__ {
|
||||
/* bar peripheral */
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Note that there's a target property that specifies the location where the
|
||||
contents of the overlay node will be placed, and it references the node
|
||||
in the foo.dts file.
|
||||
|
||||
$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
|
||||
$ fdtdump bar.dtbo
|
||||
...
|
||||
/ {
|
||||
... /* properties */
|
||||
fragment@0 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
__fixups__ {
|
||||
ocp = "/fragment@0:target:0";
|
||||
};
|
||||
};
|
||||
|
||||
No __symbols__ node has been generated (no label in bar.dts).
|
||||
Note that the target's ocp label is undefined, so the phandle
|
||||
value is filled with the illegal value '0xffffffff', while a __fixups__
|
||||
node has been generated, which marks the location in the tree where
|
||||
the label lookup should store the runtime phandle value of the ocp node.
|
||||
|
||||
The format of the __fixups__ node entry is
|
||||
|
||||
<label> = "<local-full-path>:<property-name>:<offset>"
|
||||
[, "<local-full-path>:<property-name>:<offset>"...];
|
||||
|
||||
<label> Is the label we're referring
|
||||
<local-full-path> Is the full path of the node the reference is
|
||||
<property-name> Is the name of the property containing the
|
||||
reference
|
||||
<offset> The offset (in bytes) of where the property's
|
||||
phandle value is located.
|
||||
|
||||
Doing the same with the baz peripheral's DTS format is a little bit more
|
||||
involved, since baz contains references to local labels which require
|
||||
local fixups.
|
||||
|
||||
/dts-v1/;
|
||||
/plugin/; /* allow undefined label references and record them */
|
||||
/ {
|
||||
.... /* various properties for loader use; i.e. part id etc. */
|
||||
fragment@0 {
|
||||
target = <&res>;
|
||||
__overlay__ {
|
||||
/* baz resources */
|
||||
baz_res: res_baz { ... };
|
||||
};
|
||||
};
|
||||
fragment@1 {
|
||||
target = <&ocp>;
|
||||
__overlay__ {
|
||||
/* baz peripheral */
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
/* reference to another point in the tree */
|
||||
ref-to-res = <&baz_res>;
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Note that &bar_res reference.
|
||||
|
||||
$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
|
||||
$ fdtdump baz.dtbo
|
||||
...
|
||||
/ {
|
||||
... /* properties */
|
||||
fragment@0 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
res_baz {
|
||||
....
|
||||
phandle = <0x00000001>;
|
||||
};
|
||||
};
|
||||
};
|
||||
fragment@1 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
... /* various properties and child nodes */
|
||||
ref-to-res = <0x00000001>;
|
||||
}
|
||||
};
|
||||
};
|
||||
__fixups__ {
|
||||
res = "/fragment@0:target:0";
|
||||
ocp = "/fragment@1:target:0";
|
||||
};
|
||||
__local_fixups__ {
|
||||
fragment@1 {
|
||||
__overlay__ {
|
||||
baz {
|
||||
ref-to-res = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
This is similar to the bar case, but the reference of a local label by the
|
||||
baz node generates a __local_fixups__ entry that records the place that the
|
||||
local reference is being made. No matter how phandles are allocated from dtc
|
||||
the run time loader must apply an offset to each phandle in every dynamic
|
||||
DT object loaded. The __local_fixups__ node records the offset relative to the
|
||||
start of every local reference within that property so that the loader can apply
|
||||
the offset.
|
||||
|
|
@ -193,7 +193,7 @@ particular, the following properties are desirable:
|
|||
\vdots & \multicolumn{1}{c|}{\vdots} & \\\cline{2-2}
|
||||
& \texttt{\dtendnode} \\\cline{2-2}
|
||||
& \texttt{\dtend} \\\cline{2-2}
|
||||
\multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
|
||||
\multicolumn{1}{r}{\vdots} & \multicolumn{1}{c}{\vdots} & \\\cline{2-2}
|
||||
\multicolumn{1}{r}{\emph{totalsize}} \\
|
||||
\end{tabular}
|
||||
\caption{Device tree blob layout}
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
Device Tree Source Format (version 1)
|
||||
=====================================
|
||||
|
||||
The Device Tree Source (DTS) format is a textual representation of a
|
||||
device tree in a form that can be processed by dtc into a binary
|
||||
device tree in the form expected by the kernel. The description below
|
||||
is not a formal syntax definition of DTS, but describes the basic
|
||||
constructs used to represent device trees.
|
||||
|
||||
Node and property definitions
|
||||
-----------------------------
|
||||
|
||||
Device tree nodes are defined with a node name and unit address with
|
||||
braces marking the start and end of the node definition. They may be
|
||||
preceded by a label.
|
||||
|
||||
[label:] node-name[@unit-address] {
|
||||
[properties definitions]
|
||||
[child nodes]
|
||||
}
|
||||
|
||||
Nodes may contain property definitions and/or child node
|
||||
definitions. If both are present, properties must come before child
|
||||
nodes.
|
||||
|
||||
Property definitions are name value pairs in the form:
|
||||
[label:] property-name = value;
|
||||
except for properties with empty (zero length) value which have the
|
||||
form:
|
||||
[label:] property-name;
|
||||
|
||||
Property values may be defined as an array of 8, 16, 32, or 64-bit integer
|
||||
elements, as NUL-terminated strings, as bytestrings or a combination of these.
|
||||
|
||||
* Arrays are represented by angle brackets surrounding a space separated list
|
||||
of C-style integers or character literals. Array elements default to 32-bits
|
||||
in size. An array of 32-bit elements is also known as a cell list or a list
|
||||
of cells. A cell being an unsigned 32-bit integer.
|
||||
|
||||
e.g. interrupts = <17 0xc>;
|
||||
|
||||
* A 64-bit value can be represented with two 32-bit elements.
|
||||
|
||||
e.g. clock-frequency = <0x00000001 0x00000000>;
|
||||
|
||||
* The storage size of an element can be changed using the /bits/ prefix. The
|
||||
/bits/ prefix allows for the creation of 8, 16, 32, and 64-bit elements.
|
||||
The resulting array will not be padded to a multiple of the default 32-bit
|
||||
element size.
|
||||
|
||||
e.g. interrupts = /bits/ 8 <17 0xc>;
|
||||
e.g. clock-frequency = /bits/ 64 <0x0000000100000000>;
|
||||
|
||||
* A NUL-terminated string value is represented using double quotes
|
||||
(the property value is considered to include the terminating NUL
|
||||
character).
|
||||
|
||||
e.g. compatible = "simple-bus";
|
||||
|
||||
* A bytestring is enclosed in square brackets [] with each byte
|
||||
represented by two hexadecimal digits. Spaces between each byte are
|
||||
optional.
|
||||
|
||||
e.g. local-mac-address = [00 00 12 34 56 78]; or equivalently
|
||||
local-mac-address = [000012345678];
|
||||
|
||||
* Values may have several comma-separated components, which are
|
||||
concatenated together.
|
||||
e.g. compatible = "ns16550", "ns8250";
|
||||
example = <0xf00f0000 19>, "a strange property format";
|
||||
|
||||
* In an array a reference to another node will be expanded to that node's
|
||||
phandle. References may by '&' followed by a node's label:
|
||||
e.g. interrupt-parent = < &mpic >;
|
||||
or they may be '&' followed by a node's full path in braces:
|
||||
e.g. interrupt-parent = < &{/soc/interrupt-controller@40000} >;
|
||||
References are only permitted in arrays that have an element size of
|
||||
32-bits.
|
||||
|
||||
* Outside an array, a reference to another node will be expanded to that
|
||||
node's full path.
|
||||
e.g. ethernet0 = &EMAC0;
|
||||
|
||||
* Labels may also appear before or after any component of a property
|
||||
value, or between elements of an array, or between bytes of a bytestring.
|
||||
e.g. reg = reglabel: <0 sizelabel: 0x1000000>;
|
||||
e.g. prop = [ab cd ef byte4: 00 ff fe];
|
||||
e.g. str = start: "string value" end: ;
|
||||
|
||||
|
||||
File layout
|
||||
-----------
|
||||
|
||||
Version 1 DTS files have the overall layout:
|
||||
/dts-v1/;
|
||||
|
||||
[memory reservations]
|
||||
|
||||
/ {
|
||||
[property definitions]
|
||||
[child nodes]
|
||||
};
|
||||
|
||||
* The "/dts-v1/;" must be present to identify the file as a version 1
|
||||
DTS (dts files without this tag will be treated by dtc as being in
|
||||
the obsolete "version 0", which uses a different format for integers
|
||||
amongst other small but incompatible changes).
|
||||
|
||||
* Memory reservations define an entry for the device tree blob's
|
||||
memory reservation table. They have the form:
|
||||
e.g. /memreserve/ <address> <length>;
|
||||
Where <address> and <length> are 64-bit C-style integers.
|
||||
|
||||
* The / { ... }; section defines the root node of the device tree.
|
||||
|
||||
* C style (/* ... */) and C++ style (// ...) comments are supported.
|
||||
|
||||
|
||||
|
||||
-- David Gibson <david@gibson.dropbear.id.au>
|
||||
-- Yoder Stuart <stuart.yoder@freescale.com>
|
||||
-- Anton Staaf <robotboy@chromium.org>
|
||||
|
|
@ -1,773 +0,0 @@
|
|||
Device Tree Compiler Manual
|
||||
===========================
|
||||
|
||||
I - "dtc", the device tree compiler
|
||||
1) Obtaining Sources
|
||||
1.1) Submitting Patches
|
||||
2) Description
|
||||
3) Command Line
|
||||
4) Source File
|
||||
4.1) Overview
|
||||
4.2) Properties
|
||||
4.3) Labels and References
|
||||
|
||||
II - The DT block format
|
||||
1) Header
|
||||
2) Device tree generalities
|
||||
3) Device tree "structure" block
|
||||
4) Device tree "strings" block
|
||||
|
||||
|
||||
III - libfdt
|
||||
|
||||
IV - Utility Tools
|
||||
1) convert-dtsv0 -- Conversion to Version 1
|
||||
1) fdtdump
|
||||
|
||||
|
||||
I - "dtc", the device tree compiler
|
||||
===================================
|
||||
|
||||
1) Sources
|
||||
|
||||
Source code for the Device Tree Compiler can be found at git.kernel.org.
|
||||
|
||||
The upstream repository is here:
|
||||
|
||||
git://git.kernel.org/pub/scm/utils/dtc/dtc.git
|
||||
https://git.kernel.org/pub/scm/utils/dtc/dtc.git
|
||||
|
||||
The gitweb interface for the upstream repository is:
|
||||
|
||||
https://git.kernel.org/cgit/utils/dtc/dtc.git/
|
||||
|
||||
1.1) Submitting Patches
|
||||
|
||||
Patches should be sent to the maintainer:
|
||||
David Gibson <david@gibson.dropbear.id.au>
|
||||
and CCed to <devicetree-compiler@vger.kernel.org>.
|
||||
|
||||
2) Description
|
||||
|
||||
The Device Tree Compiler, dtc, takes as input a device-tree in
|
||||
a given format and outputs a device-tree in another format.
|
||||
Typically, the input format is "dts", a human readable source
|
||||
format, and creates a "dtb", or binary format as output.
|
||||
|
||||
The currently supported Input Formats are:
|
||||
|
||||
- "dtb": "blob" format. A flattened device-tree block with
|
||||
header in one binary blob.
|
||||
|
||||
- "dts": "source" format. A text file containing a "source"
|
||||
for a device-tree.
|
||||
|
||||
- "fs" format. A representation equivalent to the output of
|
||||
/proc/device-tree where nodes are directories and
|
||||
properties are files.
|
||||
|
||||
The currently supported Output Formats are:
|
||||
|
||||
- "dtb": "blob" format
|
||||
|
||||
- "dts": "source" format
|
||||
|
||||
- "asm": assembly language file. A file that can be sourced
|
||||
by gas to generate a device-tree "blob". That file can
|
||||
then simply be added to your Makefile. Additionally, the
|
||||
assembly file exports some symbols that can be used.
|
||||
|
||||
- "yaml": DT encoded in YAML format. This representation is an
|
||||
intermediate format used for validation tools.
|
||||
|
||||
|
||||
3) Command Line
|
||||
|
||||
The syntax of the dtc command line is:
|
||||
|
||||
dtc [options] [<input_filename>]
|
||||
|
||||
Options:
|
||||
|
||||
<input_filename>
|
||||
The name of the input source file. If no <input_filename>
|
||||
or "-" is given, stdin is used.
|
||||
|
||||
-b <number>
|
||||
Set the physical boot cpu.
|
||||
|
||||
-f
|
||||
Force. Try to produce output even if the input tree has errors.
|
||||
|
||||
-h
|
||||
Emit a brief usage and help message.
|
||||
|
||||
-I <input_format>
|
||||
The source input format, as listed above.
|
||||
|
||||
-o <output_filename>
|
||||
The name of the generated output file. Use "-" for stdout.
|
||||
|
||||
-O <output_format>
|
||||
The generated output format, as listed above.
|
||||
|
||||
-d <dependency_filename>
|
||||
Generate a dependency file during compilation.
|
||||
|
||||
-q
|
||||
Quiet: -q suppress warnings, -qq errors, -qqq all
|
||||
|
||||
-R <number>
|
||||
Make space for <number> reserve map entries
|
||||
Relevant for dtb and asm output only.
|
||||
|
||||
-@
|
||||
Generates a __symbols__ node at the root node. This node contains a
|
||||
property for each label. The property's name is the label name and the
|
||||
value is the path of the labeled node.
|
||||
|
||||
-L
|
||||
Possibly generates a __local_fixups__ and a __fixups__ node at the root node.
|
||||
For each property that contains a phandle reference using a locally
|
||||
defined phandle, the __local_fixups__ node contains a property (at path
|
||||
/__local_fixups__/$a if $a is the path of the node). Its value is a list
|
||||
of offsets that are phandle values. If there are no such properties, no
|
||||
__local_fixups__ node is generated.
|
||||
For each undefined label used in at least one reference, the __fixups__
|
||||
node contains a property. Its name is the label name, its value is a
|
||||
list of locations where the label is used in a reference in the format
|
||||
"path:property:offset". If there is no undefined label, no __fixups__
|
||||
nodes is generated.
|
||||
Enabled by default for compiling overlays (i.e. dts files with a
|
||||
/plugin/ tag).
|
||||
|
||||
-A
|
||||
Generate automatically aliases for all node labels. This is similar to
|
||||
the -@ option (the __symbols__ node contain identical information) but
|
||||
the semantics are slightly different since no phandles are automatically
|
||||
generated for labeled nodes.
|
||||
|
||||
-S <bytes>
|
||||
Ensure the blob at least <bytes> long, adding additional
|
||||
space if needed.
|
||||
|
||||
-v
|
||||
Print DTC version and exit.
|
||||
|
||||
-V <output_version>
|
||||
Generate output conforming to the given <output_version>.
|
||||
By default the most recent version is generated.
|
||||
Relevant for dtb and asm output only.
|
||||
|
||||
|
||||
The <output_version> defines what version of the "blob" format will be
|
||||
generated. Supported versions are 1, 2, 3, 16 and 17. The default is
|
||||
always the most recent version and is likely the highest number.
|
||||
|
||||
Additionally, dtc performs various sanity checks on the tree.
|
||||
|
||||
|
||||
4) Device Tree Source file
|
||||
|
||||
4.1) Overview
|
||||
|
||||
Here is a very rough overview of the layout of a DTS source file:
|
||||
|
||||
|
||||
sourcefile: versioninfo plugindecl list_of_memreserve devicetree
|
||||
|
||||
memreserve: label 'memreserve' ADDR ADDR ';'
|
||||
| label 'memreserve' ADDR '-' ADDR ';'
|
||||
|
||||
devicetree: '/' nodedef
|
||||
|
||||
versioninfo: '/' 'dts-v1' '/' ';'
|
||||
|
||||
plugindecl: '/' 'plugin' '/' ';'
|
||||
| /* empty */
|
||||
|
||||
nodedef: '{' list_of_property list_of_subnode '}' ';'
|
||||
|
||||
property: label PROPNAME '=' propdata ';'
|
||||
|
||||
propdata: STRING
|
||||
| '<' list_of_cells '>'
|
||||
| '[' list_of_bytes ']'
|
||||
|
||||
subnode: label nodename nodedef
|
||||
|
||||
That structure forms a hierarchical layout of nodes and properties
|
||||
rooted at an initial node as:
|
||||
|
||||
/ {
|
||||
}
|
||||
|
||||
Both classic C style and C++ style comments are supported.
|
||||
|
||||
Source files may be directly included using the syntax:
|
||||
|
||||
/include/ "filename"
|
||||
|
||||
|
||||
4.2) Properties
|
||||
|
||||
Properties are named, possibly labeled, values. Each value
|
||||
is one of:
|
||||
|
||||
- A null-teminated C-like string,
|
||||
- A numeric value fitting in 32 bits,
|
||||
- A list of 32-bit values
|
||||
- A byte sequence
|
||||
|
||||
Here are some example property definitions:
|
||||
|
||||
- A property containing a 0 terminated string
|
||||
|
||||
property1 = "string_value";
|
||||
|
||||
- A property containing a numerical 32-bit hexadecimal value
|
||||
|
||||
property2 = <1234abcd>;
|
||||
|
||||
- A property containing 3 numerical 32-bit hexadecimal values
|
||||
|
||||
property3 = <12345678 12345678 deadbeef>;
|
||||
|
||||
- A property whose content is an arbitrary array of bytes
|
||||
|
||||
property4 = [0a 0b 0c 0d de ea ad be ef];
|
||||
|
||||
|
||||
Node may contain sub-nodes to obtain a hierarchical structure.
|
||||
For example:
|
||||
|
||||
- A child node named "childnode" whose unit name is
|
||||
"childnode at address". It in turn has a string property
|
||||
called "childprop".
|
||||
|
||||
childnode@address {
|
||||
childprop = "hello\n";
|
||||
};
|
||||
|
||||
|
||||
By default, all numeric values are hexadecimal. Alternate bases
|
||||
may be specified using a prefix "d#" for decimal, "b#" for binary,
|
||||
and "o#" for octal.
|
||||
|
||||
Strings support common escape sequences from C: "\n", "\t", "\r",
|
||||
"\(octal value)", "\x(hex value)".
|
||||
|
||||
|
||||
4.3) Labels and References
|
||||
|
||||
Labels may be applied to nodes or properties. Labels appear
|
||||
before a node name, and are referenced using an ampersand: &label.
|
||||
Absolute node path names are also allowed in node references.
|
||||
|
||||
In this example, a node is labeled "mpic" and then referenced:
|
||||
|
||||
mpic: interrupt-controller@40000 {
|
||||
...
|
||||
};
|
||||
|
||||
ethernet-phy@3 {
|
||||
interrupt-parent = <&mpic>;
|
||||
...
|
||||
};
|
||||
|
||||
And used in properties, labels may appear before or after any value:
|
||||
|
||||
randomnode {
|
||||
prop: string = data: "mystring\n" data_end: ;
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
|
||||
II - The DT block format
|
||||
========================
|
||||
|
||||
This chapter defines the format of the flattened device-tree
|
||||
passed to the kernel. The actual content of the device tree
|
||||
are described in the kernel documentation in the file
|
||||
|
||||
linux-2.6/Documentation/powerpc/booting-without-of.txt
|
||||
|
||||
You can find example of code manipulating that format within
|
||||
the kernel. For example, the file:
|
||||
|
||||
including arch/powerpc/kernel/prom_init.c
|
||||
|
||||
will generate a flattened device-tree from the Open Firmware
|
||||
representation. Other utilities such as fs2dt, which is part of
|
||||
the kexec tools, will generate one from a filesystem representation.
|
||||
Some bootloaders such as U-Boot provide a bit more support by
|
||||
using the libfdt code.
|
||||
|
||||
For booting the kernel, the device tree block has to be in main memory.
|
||||
It has to be accessible in both real mode and virtual mode with no
|
||||
mapping other than main memory. If you are writing a simple flash
|
||||
bootloader, it should copy the block to RAM before passing it to
|
||||
the kernel.
|
||||
|
||||
|
||||
1) Header
|
||||
---------
|
||||
|
||||
The kernel is entered with r3 pointing to an area of memory that is
|
||||
roughly described in include/asm-powerpc/prom.h by the structure
|
||||
boot_param_header:
|
||||
|
||||
struct boot_param_header {
|
||||
u32 magic; /* magic word OF_DT_HEADER */
|
||||
u32 totalsize; /* total size of DT block */
|
||||
u32 off_dt_struct; /* offset to structure */
|
||||
u32 off_dt_strings; /* offset to strings */
|
||||
u32 off_mem_rsvmap; /* offset to memory reserve map */
|
||||
u32 version; /* format version */
|
||||
u32 last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
u32 boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
u32 size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
u32 size_dt_struct; /* size of the DT structure block */
|
||||
};
|
||||
|
||||
Along with the constants:
|
||||
|
||||
/* Definitions used by the flattened device tree */
|
||||
#define OF_DT_HEADER 0xd00dfeed /* 4: version,
|
||||
4: total size */
|
||||
#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name
|
||||
*/
|
||||
#define OF_DT_END_NODE 0x2 /* End node */
|
||||
#define OF_DT_PROP 0x3 /* Property: name off,
|
||||
size, content */
|
||||
#define OF_DT_END 0x9
|
||||
|
||||
All values in this header are in big endian format, the various
|
||||
fields in this header are defined more precisely below. All "offset"
|
||||
values are in bytes from the start of the header; that is from the
|
||||
value of r3.
|
||||
|
||||
- magic
|
||||
|
||||
This is a magic value that "marks" the beginning of the
|
||||
device-tree block header. It contains the value 0xd00dfeed and is
|
||||
defined by the constant OF_DT_HEADER
|
||||
|
||||
- totalsize
|
||||
|
||||
This is the total size of the DT block including the header. The
|
||||
"DT" block should enclose all data structures defined in this
|
||||
chapter (who are pointed to by offsets in this header). That is,
|
||||
the device-tree structure, strings, and the memory reserve map.
|
||||
|
||||
- off_dt_struct
|
||||
|
||||
This is an offset from the beginning of the header to the start
|
||||
of the "structure" part the device tree. (see 2) device tree)
|
||||
|
||||
- off_dt_strings
|
||||
|
||||
This is an offset from the beginning of the header to the start
|
||||
of the "strings" part of the device-tree
|
||||
|
||||
- off_mem_rsvmap
|
||||
|
||||
This is an offset from the beginning of the header to the start
|
||||
of the reserved memory map. This map is a list of pairs of 64-
|
||||
bit integers. Each pair is a physical address and a size. The
|
||||
list is terminated by an entry of size 0. This map provides the
|
||||
kernel with a list of physical memory areas that are "reserved"
|
||||
and thus not to be used for memory allocations, especially during
|
||||
early initialization. The kernel needs to allocate memory during
|
||||
boot for things like un-flattening the device-tree, allocating an
|
||||
MMU hash table, etc... Those allocations must be done in such a
|
||||
way to avoid overriding critical things like, on Open Firmware
|
||||
capable machines, the RTAS instance, or on some pSeries, the TCE
|
||||
tables used for the iommu. Typically, the reserve map should
|
||||
contain _at least_ this DT block itself (header,total_size). If
|
||||
you are passing an initrd to the kernel, you should reserve it as
|
||||
well. You do not need to reserve the kernel image itself. The map
|
||||
should be 64-bit aligned.
|
||||
|
||||
- version
|
||||
|
||||
This is the version of this structure. Version 1 stops
|
||||
here. Version 2 adds an additional field boot_cpuid_phys.
|
||||
Version 3 adds the size of the strings block, allowing the kernel
|
||||
to reallocate it easily at boot and free up the unused flattened
|
||||
structure after expansion. Version 16 introduces a new more
|
||||
"compact" format for the tree itself that is however not backward
|
||||
compatible. Version 17 adds an additional field, size_dt_struct,
|
||||
allowing it to be reallocated or moved more easily (this is
|
||||
particularly useful for bootloaders which need to make
|
||||
adjustments to a device tree based on probed information). You
|
||||
should always generate a structure of the highest version defined
|
||||
at the time of your implementation. Currently that is version 17,
|
||||
unless you explicitly aim at being backward compatible.
|
||||
|
||||
- last_comp_version
|
||||
|
||||
Last compatible version. This indicates down to what version of
|
||||
the DT block you are backward compatible. For example, version 2
|
||||
is backward compatible with version 1 (that is, a kernel build
|
||||
for version 1 will be able to boot with a version 2 format). You
|
||||
should put a 1 in this field if you generate a device tree of
|
||||
version 1 to 3, or 16 if you generate a tree of version 16 or 17
|
||||
using the new unit name format.
|
||||
|
||||
- boot_cpuid_phys
|
||||
|
||||
This field only exist on version 2 headers. It indicate which
|
||||
physical CPU ID is calling the kernel entry point. This is used,
|
||||
among others, by kexec. If you are on an SMP system, this value
|
||||
should match the content of the "reg" property of the CPU node in
|
||||
the device-tree corresponding to the CPU calling the kernel entry
|
||||
point (see further chapters for more information on the required
|
||||
device-tree contents)
|
||||
|
||||
- size_dt_strings
|
||||
|
||||
This field only exists on version 3 and later headers. It
|
||||
gives the size of the "strings" section of the device tree (which
|
||||
starts at the offset given by off_dt_strings).
|
||||
|
||||
- size_dt_struct
|
||||
|
||||
This field only exists on version 17 and later headers. It gives
|
||||
the size of the "structure" section of the device tree (which
|
||||
starts at the offset given by off_dt_struct).
|
||||
|
||||
So the typical layout of a DT block (though the various parts don't
|
||||
need to be in that order) looks like this (addresses go from top to
|
||||
bottom):
|
||||
|
||||
------------------------------
|
||||
r3 -> | struct boot_param_header |
|
||||
------------------------------
|
||||
| (alignment gap) (*) |
|
||||
------------------------------
|
||||
| memory reserve map |
|
||||
------------------------------
|
||||
| (alignment gap) |
|
||||
------------------------------
|
||||
| |
|
||||
| device-tree structure |
|
||||
| |
|
||||
------------------------------
|
||||
| (alignment gap) |
|
||||
------------------------------
|
||||
| |
|
||||
| device-tree strings |
|
||||
| |
|
||||
-----> ------------------------------
|
||||
|
|
||||
|
|
||||
--- (r3 + totalsize)
|
||||
|
||||
(*) The alignment gaps are not necessarily present; their presence
|
||||
and size are dependent on the various alignment requirements of
|
||||
the individual data blocks.
|
||||
|
||||
|
||||
2) Device tree generalities
|
||||
---------------------------
|
||||
|
||||
This device-tree itself is separated in two different blocks, a
|
||||
structure block and a strings block. Both need to be aligned to a 4
|
||||
byte boundary.
|
||||
|
||||
First, let's quickly describe the device-tree concept before detailing
|
||||
the storage format. This chapter does _not_ describe the detail of the
|
||||
required types of nodes & properties for the kernel, this is done
|
||||
later in chapter III.
|
||||
|
||||
The device-tree layout is strongly inherited from the definition of
|
||||
the Open Firmware IEEE 1275 device-tree. It's basically a tree of
|
||||
nodes, each node having two or more named properties. A property can
|
||||
have a value or not.
|
||||
|
||||
It is a tree, so each node has one and only one parent except for the
|
||||
root node who has no parent.
|
||||
|
||||
A node has 2 names. The actual node name is generally contained in a
|
||||
property of type "name" in the node property list whose value is a
|
||||
zero terminated string and is mandatory for version 1 to 3 of the
|
||||
format definition (as it is in Open Firmware). Version 16 makes it
|
||||
optional as it can generate it from the unit name defined below.
|
||||
|
||||
There is also a "unit name" that is used to differentiate nodes with
|
||||
the same name at the same level, it is usually made of the node
|
||||
names, the "@" sign, and a "unit address", which definition is
|
||||
specific to the bus type the node sits on.
|
||||
|
||||
The unit name doesn't exist as a property per-se but is included in
|
||||
the device-tree structure. It is typically used to represent "path" in
|
||||
the device-tree. More details about the actual format of these will be
|
||||
below.
|
||||
|
||||
The kernel powerpc generic code does not make any formal use of the
|
||||
unit address (though some board support code may do) so the only real
|
||||
requirement here for the unit address is to ensure uniqueness of
|
||||
the node unit name at a given level of the tree. Nodes with no notion
|
||||
of address and no possible sibling of the same name (like /memory or
|
||||
/cpus) may omit the unit address in the context of this specification,
|
||||
or use the "@0" default unit address. The unit name is used to define
|
||||
a node "full path", which is the concatenation of all parent node
|
||||
unit names separated with "/".
|
||||
|
||||
The root node doesn't have a defined name, and isn't required to have
|
||||
a name property either if you are using version 3 or earlier of the
|
||||
format. It also has no unit address (no @ symbol followed by a unit
|
||||
address). The root node unit name is thus an empty string. The full
|
||||
path to the root node is "/".
|
||||
|
||||
Every node which actually represents an actual device (that is, a node
|
||||
which isn't only a virtual "container" for more nodes, like "/cpus"
|
||||
is) is also required to have a "device_type" property indicating the
|
||||
type of node .
|
||||
|
||||
Finally, every node that can be referenced from a property in another
|
||||
node is required to have a "linux,phandle" property. Real open
|
||||
firmware implementations provide a unique "phandle" value for every
|
||||
node that the "prom_init()" trampoline code turns into
|
||||
"linux,phandle" properties. However, this is made optional if the
|
||||
flattened device tree is used directly. An example of a node
|
||||
referencing another node via "phandle" is when laying out the
|
||||
interrupt tree which will be described in a further version of this
|
||||
document.
|
||||
|
||||
This "linux, phandle" property is a 32-bit value that uniquely
|
||||
identifies a node. You are free to use whatever values or system of
|
||||
values, internal pointers, or whatever to generate these, the only
|
||||
requirement is that every node for which you provide that property has
|
||||
a unique value for it.
|
||||
|
||||
Here is an example of a simple device-tree. In this example, an "o"
|
||||
designates a node followed by the node unit name. Properties are
|
||||
presented with their name followed by their content. "content"
|
||||
represents an ASCII string (zero terminated) value, while <content>
|
||||
represents a 32-bit hexadecimal value. The various nodes in this
|
||||
example will be discussed in a later chapter. At this point, it is
|
||||
only meant to give you a idea of what a device-tree looks like. I have
|
||||
purposefully kept the "name" and "linux,phandle" properties which
|
||||
aren't necessary in order to give you a better idea of what the tree
|
||||
looks like in practice.
|
||||
|
||||
/ o device-tree
|
||||
|- name = "device-tree"
|
||||
|- model = "MyBoardName"
|
||||
|- compatible = "MyBoardFamilyName"
|
||||
|- #address-cells = <2>
|
||||
|- #size-cells = <2>
|
||||
|- linux,phandle = <0>
|
||||
|
|
||||
o cpus
|
||||
| | - name = "cpus"
|
||||
| | - linux,phandle = <1>
|
||||
| | - #address-cells = <1>
|
||||
| | - #size-cells = <0>
|
||||
| |
|
||||
| o PowerPC,970@0
|
||||
| |- name = "PowerPC,970"
|
||||
| |- device_type = "cpu"
|
||||
| |- reg = <0>
|
||||
| |- clock-frequency = <5f5e1000>
|
||||
| |- 64-bit
|
||||
| |- linux,phandle = <2>
|
||||
|
|
||||
o memory@0
|
||||
| |- name = "memory"
|
||||
| |- device_type = "memory"
|
||||
| |- reg = <00000000 00000000 00000000 20000000>
|
||||
| |- linux,phandle = <3>
|
||||
|
|
||||
o chosen
|
||||
|- name = "chosen"
|
||||
|- bootargs = "root=/dev/sda2"
|
||||
|- linux,phandle = <4>
|
||||
|
||||
This tree is almost a minimal tree. It pretty much contains the
|
||||
minimal set of required nodes and properties to boot a linux kernel;
|
||||
that is, some basic model information at the root, the CPUs, and the
|
||||
physical memory layout. It also includes misc information passed
|
||||
through /chosen, like in this example, the platform type (mandatory)
|
||||
and the kernel command line arguments (optional).
|
||||
|
||||
The /cpus/PowerPC,970@0/64-bit property is an example of a
|
||||
property without a value. All other properties have a value. The
|
||||
significance of the #address-cells and #size-cells properties will be
|
||||
explained in chapter IV which defines precisely the required nodes and
|
||||
properties and their content.
|
||||
|
||||
|
||||
3) Device tree "structure" block
|
||||
|
||||
The structure of the device tree is a linearized tree structure. The
|
||||
"OF_DT_BEGIN_NODE" token starts a new node, and the "OF_DT_END_NODE"
|
||||
ends that node definition. Child nodes are simply defined before
|
||||
"OF_DT_END_NODE" (that is nodes within the node). A 'token' is a 32
|
||||
bit value. The tree has to be "finished" with a OF_DT_END token
|
||||
|
||||
Here's the basic structure of a single node:
|
||||
|
||||
* token OF_DT_BEGIN_NODE (that is 0x00000001)
|
||||
* for version 1 to 3, this is the node full path as a zero
|
||||
terminated string, starting with "/". For version 16 and later,
|
||||
this is the node unit name only (or an empty string for the
|
||||
root node)
|
||||
* [align gap to next 4 bytes boundary]
|
||||
* for each property:
|
||||
* token OF_DT_PROP (that is 0x00000003)
|
||||
* 32-bit value of property value size in bytes (or 0 if no
|
||||
value)
|
||||
* 32-bit value of offset in string block of property name
|
||||
* property value data if any
|
||||
* [align gap to next 4 bytes boundary]
|
||||
* [child nodes if any]
|
||||
* token OF_DT_END_NODE (that is 0x00000002)
|
||||
|
||||
So the node content can be summarized as a start token, a full path,
|
||||
a list of properties, a list of child nodes, and an end token. Every
|
||||
child node is a full node structure itself as defined above.
|
||||
|
||||
NOTE: The above definition requires that all property definitions for
|
||||
a particular node MUST precede any subnode definitions for that node.
|
||||
Although the structure would not be ambiguous if properties and
|
||||
subnodes were intermingled, the kernel parser requires that the
|
||||
properties come first (up until at least 2.6.22). Any tools
|
||||
manipulating a flattened tree must take care to preserve this
|
||||
constraint.
|
||||
|
||||
4) Device tree "strings" block
|
||||
|
||||
In order to save space, property names, which are generally redundant,
|
||||
are stored separately in the "strings" block. This block is simply the
|
||||
whole bunch of zero terminated strings for all property names
|
||||
concatenated together. The device-tree property definitions in the
|
||||
structure block will contain offset values from the beginning of the
|
||||
strings block.
|
||||
|
||||
|
||||
III - libfdt
|
||||
============
|
||||
|
||||
This library should be merged into dtc proper.
|
||||
This library should likely be worked into U-Boot and the kernel.
|
||||
|
||||
|
||||
IV - Utility Tools
|
||||
==================
|
||||
|
||||
1) convert-dtsv0 -- Conversion to Version 1
|
||||
|
||||
convert-dtsv0 is a small utility program which converts (DTS)
|
||||
Device Tree Source from the obsolete version 0 to version 1.
|
||||
|
||||
Version 1 DTS files are marked by line "/dts-v1/;" at the top of the file.
|
||||
|
||||
The syntax of the convert-dtsv0 command line is:
|
||||
|
||||
convert-dtsv0 [<input_filename ... >]
|
||||
|
||||
Each file passed will be converted to the new /dts-v1/ version by creating
|
||||
a new file with a "v1" appended the filename.
|
||||
|
||||
Comments, empty lines, etc. are preserved.
|
||||
|
||||
|
||||
2) fdtdump -- Flat Device Tree dumping utility
|
||||
|
||||
The fdtdump program prints a readable version of a flat device tree file.
|
||||
|
||||
The syntax of the fdtdump command line is:
|
||||
|
||||
fdtdump [options] <DTB-file-name>
|
||||
|
||||
Where options are:
|
||||
-d,--debug Dump debug information while decoding the file
|
||||
-s,--scan Scan for an embedded fdt in given file
|
||||
|
||||
3) fdtoverlay -- Flat Device Tree overlay applicator
|
||||
|
||||
The fdtoverlay applies an arbitrary number of FDT overlays to a base FDT blob
|
||||
to a given output file.
|
||||
|
||||
The syntax of the fdtoverlay command line is:
|
||||
|
||||
fdtoverlay -i <base-blob> -o <output-blob> <overlay-blob0> [<overlay-blob1> ...]
|
||||
|
||||
Where options are:
|
||||
-i, --input Input base DT blob
|
||||
-o, --output Output DT blob
|
||||
-v, --verbose Verbose message output
|
||||
|
||||
4 ) fdtget -- Read properties from device tree
|
||||
|
||||
This command can be used to obtain individual values from the device tree in a
|
||||
nicely formatted way. You can specify multiple nodes to display (when using -p)
|
||||
or multiple node/property pairs (when not using -p). For the latter, each
|
||||
property is displayed on its own line, with a space between each cell within
|
||||
the property.
|
||||
|
||||
The syntax of the fdtget command is:
|
||||
|
||||
fdtget <options> <dt file> [<node> <property>]...
|
||||
fdtget -p <options> <dt file> [<node> ]...
|
||||
|
||||
where options are:
|
||||
|
||||
<type> s=string, i=int, u=unsigned, x=hex, r=raw
|
||||
Optional modifier prefix:
|
||||
hh or b=byte, h=2 byte, l=4 byte (default)
|
||||
|
||||
Options: -[t:pld:hV]
|
||||
-t, --type <arg> Type of data
|
||||
-p, --properties List properties for each node
|
||||
-l, --list List subnodes for each node
|
||||
-d, --default <arg> Default value to display when the property is missing
|
||||
-h, --help Print this help and exit
|
||||
-V, --version Print version and exit
|
||||
|
||||
If -t is not provided, fdtget will try to figure out the type, trying to detect
|
||||
strings, string lists and the size of each value in the property. This is
|
||||
similar to how fdtdump works, and uses the same heuristics.
|
||||
|
||||
|
||||
5 ) fdtput - Write properties to a device tree
|
||||
|
||||
The syntax of the fdtput command is:
|
||||
|
||||
fdtput <options> <dt file> <node> <property> [<value>...]
|
||||
fdtput -c <options> <dt file> [<node>...]
|
||||
fdtput -r <options> <dt file> [<node>...]
|
||||
fdtput -d <options> <dt file> <node> [<property>...]
|
||||
|
||||
Options are:
|
||||
|
||||
<type> s=string, i=int, u=unsigned, x=hex
|
||||
Optional modifier prefix:
|
||||
hh or b=byte, h=2 byte, l=4 byte (default)
|
||||
|
||||
-c, --create Create nodes if they don't already exist
|
||||
-r, --remove Delete nodes (and any subnodes) if they already exist
|
||||
-d, --delete Delete properties if they already exist
|
||||
-p, --auto-path Automatically create nodes as needed for the node path
|
||||
-t, --type <arg> Type of data
|
||||
-v, --verbose Display each value decoded from command line
|
||||
-h, --help Print this help and exit
|
||||
-V, --version Print version and exit
|
||||
|
||||
The option determines which usage is selected and therefore the operation that
|
||||
is performed. The first usage adds or updates properties; the rest are used to
|
||||
create/delete nodes and delete properties.
|
||||
|
||||
For the first usage, the command line arguments are joined together into a
|
||||
single value which is written to the property. The -t option is required so
|
||||
that fdtput knows how to decode its arguments.
|
||||
369
Makefile
369
Makefile
|
|
@ -1,363 +1,42 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Device Tree Compiler
|
||||
#
|
||||
|
||||
$(warning WARNING: Building dtc using make is deprecated, in favour of using Meson (https://mesonbuild.com))
|
||||
$(warning )
|
||||
$(warning Use `meson setup builddir/ && meson compile -C builddir/` to build, `meson test -C builddir/` to test, or `meson configure` to see build options.)
|
||||
|
||||
#
|
||||
# Version information will be constructed in this order:
|
||||
# DTC_VERSION release version as MAJOR.MINOR.PATCH
|
||||
# LOCAL_VERSION is likely from command line.
|
||||
# CONFIG_LOCALVERSION from some future config system.
|
||||
#
|
||||
DTC_VERSION = $(shell cat VERSION.txt)
|
||||
LOCAL_VERSION =
|
||||
CONFIG_LOCALVERSION =
|
||||
|
||||
# Control the assumptions made (e.g. risking security issues) in the code.
|
||||
# See libfdt_internal.h for details
|
||||
ASSUME_MASK ?= 0
|
||||
|
||||
CPPFLAGS = -I libfdt -I . -DFDT_ASSUME_MASK=$(ASSUME_MASK)
|
||||
WARNINGS = -Wall -Wpointer-arith -Wcast-qual -Wnested-externs -Wsign-compare \
|
||||
-Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wshadow \
|
||||
-Wwrite-strings
|
||||
ifeq ($(shell $(CC) --version | grep -q gcc && echo gcc),gcc)
|
||||
WARNINGS += -Wsuggest-attribute=format
|
||||
endif
|
||||
CFLAGS = -g -Os $(SHAREDLIB_CFLAGS) -Werror $(WARNINGS) $(EXTRA_CFLAGS)
|
||||
TARGETS = dtc ftdump
|
||||
CFLAGS = -Wall -g
|
||||
|
||||
BISON = bison
|
||||
LEX = flex
|
||||
SWIG = swig
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = $(INSTALL)
|
||||
INSTALL_LIB = $(INSTALL)
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
INSTALL_SCRIPT = $(INSTALL)
|
||||
DESTDIR =
|
||||
PREFIX = $(HOME)
|
||||
BINDIR = $(PREFIX)/bin
|
||||
LIBDIR = $(PREFIX)/lib
|
||||
INCLUDEDIR = $(PREFIX)/include
|
||||
DTC_OBJS = dtc.o livetree.o flattree.o data.o treesource.o fstree.o \
|
||||
dtc-parser.tab.o lex.yy.o
|
||||
|
||||
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
|
||||
sed -e 's/\(cygwin\|msys\).*/\1/')
|
||||
OBJS = $(DTC_OBJS) libdt.o ftdump.o
|
||||
|
||||
NO_VALGRIND := $(shell $(PKG_CONFIG) --exists valgrind; echo $$?)
|
||||
ifeq ($(NO_VALGRIND),1)
|
||||
CPPFLAGS += -DNO_VALGRIND
|
||||
else
|
||||
CFLAGS += $(shell $(PKG_CONFIG) --cflags valgrind)
|
||||
endif
|
||||
|
||||
# libyaml before version 0.2.3 expects non-const string parameters. Supporting
|
||||
# both variants would require either cpp magic or passing
|
||||
# -Wno-error=discarded-qualifiers to the compiler. For the sake of simplicity
|
||||
# just support libyaml >= 0.2.3.
|
||||
NO_YAML := $(shell $(PKG_CONFIG) --atleast-version 0.2.3 yaml-0.1; echo $$?)
|
||||
ifeq ($(NO_YAML),1)
|
||||
CFLAGS += -DNO_YAML
|
||||
else
|
||||
LDLIBS_dtc += $(shell $(PKG_CONFIG) --libs yaml-0.1)
|
||||
CFLAGS += $(shell $(PKG_CONFIG) --cflags yaml-0.1)
|
||||
endif
|
||||
|
||||
HAS_VERSION_SCRIPT := $(shell echo 'int main(){}' | $(CC) -Wl,--version-script=/dev/null -x c - -o /dev/null 2>/dev/null && echo y)
|
||||
|
||||
ifeq ($(HOSTOS),darwin)
|
||||
SHAREDLIB_EXT = dylib
|
||||
SHAREDLIB_CFLAGS = -fPIC
|
||||
SHAREDLIB_LDFLAGS = -fPIC -dynamiclib -Wl,-install_name -Wl,
|
||||
else ifeq ($(HOSTOS),$(filter $(HOSTOS),msys cygwin))
|
||||
SHAREDLIB_EXT = so
|
||||
SHAREDLIB_CFLAGS =
|
||||
SHAREDLIB_LDFLAGS = -shared -Wl,-soname,
|
||||
else
|
||||
SHAREDLIB_EXT = so
|
||||
SHAREDLIB_CFLAGS = -fPIC
|
||||
SHAREDLIB_LDFLAGS = -fPIC -shared -Wl,-soname,
|
||||
endif
|
||||
|
||||
ifeq ($(HAS_VERSION_SCRIPT),y)
|
||||
SHAREDLIB_LDFLAGS += -Wl,--version-script=$(LIBFDT_version)
|
||||
endif
|
||||
|
||||
#
|
||||
# Overall rules
|
||||
#
|
||||
ifdef V
|
||||
VECHO = :
|
||||
else
|
||||
VECHO = echo " "
|
||||
ARFLAGS = rc
|
||||
.SILENT:
|
||||
endif
|
||||
|
||||
NODEPTARGETS = clean
|
||||
ifeq ($(MAKECMDGOALS),)
|
||||
DEPTARGETS = all
|
||||
else
|
||||
DEPTARGETS = $(filter-out $(NODEPTARGETS),$(MAKECMDGOALS))
|
||||
endif
|
||||
|
||||
#
|
||||
# Rules for versioning
|
||||
#
|
||||
|
||||
VERSION_FILE = version_gen.h
|
||||
|
||||
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
|
||||
else if [ -x /bin/bash ]; then echo /bin/bash; \
|
||||
else echo sh; fi ; fi)
|
||||
|
||||
nullstring :=
|
||||
space := $(nullstring) # end of line
|
||||
|
||||
localver_config = $(subst $(space),, $(string) \
|
||||
$(patsubst "%",%,$(CONFIG_LOCALVERSION)))
|
||||
|
||||
localver_cmd = $(subst $(space),, $(string) \
|
||||
$(patsubst "%",%,$(LOCALVERSION)))
|
||||
|
||||
localver_scm = $(shell $(CONFIG_SHELL) ./scripts/setlocalversion)
|
||||
localver_full = $(localver_config)$(localver_cmd)$(localver_scm)
|
||||
|
||||
dtc_version = $(DTC_VERSION)$(localver_full)
|
||||
|
||||
# Contents of the generated version file.
|
||||
define filechk_version
|
||||
(echo "#define DTC_VERSION \"DTC $(dtc_version)\""; )
|
||||
endef
|
||||
|
||||
define filechk
|
||||
set -e; \
|
||||
echo ' CHK $@'; \
|
||||
mkdir -p $(dir $@); \
|
||||
$(filechk_$(1)) < $< > $@.tmp; \
|
||||
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
|
||||
rm -f $@.tmp; \
|
||||
else \
|
||||
echo ' UPD $@'; \
|
||||
mv -f $@.tmp $@; \
|
||||
fi;
|
||||
endef
|
||||
|
||||
|
||||
include Makefile.convert-dtsv0
|
||||
include Makefile.dtc
|
||||
include Makefile.utils
|
||||
|
||||
BIN += convert-dtsv0
|
||||
BIN += dtc
|
||||
BIN += fdtdump
|
||||
BIN += fdtget
|
||||
BIN += fdtput
|
||||
BIN += fdtoverlay
|
||||
|
||||
SCRIPTS = dtdiff
|
||||
|
||||
all: $(BIN) libfdt
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
ifneq ($(MAKECMDGOALS),libfdt)
|
||||
-include $(DTC_OBJS:%.o=%.d)
|
||||
-include $(CONVERT_OBJS:%.o=%.d)
|
||||
-include $(FDTDUMP_OBJS:%.o=%.d)
|
||||
-include $(FDTGET_OBJS:%.o=%.d)
|
||||
-include $(FDTPUT_OBJS:%.o=%.d)
|
||||
-include $(FDTOVERLAY_OBJS:%.o=%.d)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Rules for libfdt
|
||||
#
|
||||
LIBFDT_dir = libfdt
|
||||
LIBFDT_archive = $(LIBFDT_dir)/libfdt.a
|
||||
LIBFDT_lib = $(LIBFDT_dir)/$(LIBFDT_LIB)
|
||||
LIBFDT_include = $(addprefix $(LIBFDT_dir)/,$(LIBFDT_INCLUDES))
|
||||
LIBFDT_version = $(addprefix $(LIBFDT_dir)/,$(LIBFDT_VERSION))
|
||||
|
||||
ifeq ($(STATIC_BUILD),1)
|
||||
CFLAGS += -static
|
||||
LIBFDT_dep = $(LIBFDT_archive)
|
||||
else
|
||||
LIBFDT_dep = $(LIBFDT_lib)
|
||||
endif
|
||||
|
||||
include $(LIBFDT_dir)/Makefile.libfdt
|
||||
|
||||
.PHONY: libfdt
|
||||
libfdt: $(LIBFDT_archive) $(LIBFDT_lib)
|
||||
|
||||
$(LIBFDT_archive): $(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS))
|
||||
|
||||
$(LIBFDT_lib): $(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS)) $(LIBFDT_version)
|
||||
@$(VECHO) LD $@
|
||||
$(CC) $(LDFLAGS) $(SHAREDLIB_LDFLAGS)$(LIBFDT_soname) -o $(LIBFDT_lib) \
|
||||
$(addprefix $(LIBFDT_dir)/,$(LIBFDT_OBJS))
|
||||
ln -sf $(LIBFDT_LIB) $(LIBFDT_dir)/$(LIBFDT_soname)
|
||||
ln -sf $(LIBFDT_soname) $(LIBFDT_dir)/$(LIBFDT_so)
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include $(LIBFDT_OBJS:%.o=$(LIBFDT_dir)/%.d)
|
||||
endif
|
||||
|
||||
# This stops make from generating the lex and bison output during
|
||||
# auto-dependency computation, but throwing them away as an
|
||||
# intermediate target and building them again "for real"
|
||||
.SECONDARY: $(DTC_GEN_SRCS) $(CONVERT_GEN_SRCS)
|
||||
|
||||
install-bin: all $(SCRIPTS)
|
||||
@$(VECHO) INSTALL-BIN
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_PROGRAM) $(BIN) $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_SCRIPT) $(SCRIPTS) $(DESTDIR)$(BINDIR)
|
||||
|
||||
install-lib: libfdt
|
||||
@$(VECHO) INSTALL-LIB
|
||||
$(INSTALL) -d $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_LIB) $(LIBFDT_lib) $(DESTDIR)$(LIBDIR)
|
||||
ln -sf $(notdir $(LIBFDT_lib)) $(DESTDIR)$(LIBDIR)/$(LIBFDT_soname)
|
||||
ln -sf $(LIBFDT_soname) $(DESTDIR)$(LIBDIR)/libfdt.$(SHAREDLIB_EXT)
|
||||
$(INSTALL_DATA) $(LIBFDT_archive) $(DESTDIR)$(LIBDIR)
|
||||
|
||||
install-includes:
|
||||
@$(VECHO) INSTALL-INC
|
||||
$(INSTALL) -d $(DESTDIR)$(INCLUDEDIR)
|
||||
$(INSTALL_DATA) $(LIBFDT_include) $(DESTDIR)$(INCLUDEDIR)
|
||||
|
||||
install: install-bin install-lib install-includes
|
||||
|
||||
$(VERSION_FILE): Makefile FORCE
|
||||
$(call filechk,version)
|
||||
DEPFILES = $(DTC_OBJS:.o=.d)
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
dtc: $(DTC_OBJS)
|
||||
|
||||
convert-dtsv0: $(CONVERT_OBJS)
|
||||
@$(VECHO) LD $@
|
||||
$(LINK.c) -o $@ $^
|
||||
|
||||
fdtdump: $(FDTDUMP_OBJS)
|
||||
ftdump: ftdump.o
|
||||
$(LINK.c) -o $@ $^
|
||||
|
||||
fdtget: $(FDTGET_OBJS) $(LIBFDT_dep)
|
||||
dtc-parser.tab.c dtc-parser.tab.h dtc-parser.output: dtc-parser.y
|
||||
$(BISON) -d $<
|
||||
|
||||
fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep)
|
||||
lex.yy.c: dtc-lexer.l
|
||||
$(LEX) $<
|
||||
|
||||
fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_dep)
|
||||
lex.yy.o: lex.yy.c dtc-parser.tab.h
|
||||
|
||||
dist:
|
||||
git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \
|
||||
> ../dtc-$(dtc_version).tar
|
||||
cat ../dtc-$(dtc_version).tar | \
|
||||
gzip -9 > ../dtc-$(dtc_version).tar.gz
|
||||
check: all
|
||||
cd tests && $(MAKE) check
|
||||
|
||||
|
||||
#
|
||||
# Release signing and uploading
|
||||
# This is for maintainer convenience, don't try this at home.
|
||||
#
|
||||
ifeq ($(MAINTAINER),y)
|
||||
GPG = gpg2
|
||||
KUP = kup
|
||||
KUPDIR = /pub/software/utils/dtc
|
||||
|
||||
kup: dist
|
||||
$(GPG) --detach-sign --armor -o ../dtc-$(dtc_version).tar.sign \
|
||||
../dtc-$(dtc_version).tar
|
||||
$(KUP) put ../dtc-$(dtc_version).tar.gz ../dtc-$(dtc_version).tar.sign \
|
||||
$(KUPDIR)/dtc-$(dtc_version).tar.gz
|
||||
endif
|
||||
|
||||
tags: FORCE
|
||||
rm -f tags
|
||||
find . \( -name tests -type d -prune \) -o \
|
||||
\( ! -name '*.tab.[ch]' ! -name '*.lex.c' \
|
||||
-name '*.[chly]' -type f -print \) | xargs ctags -a
|
||||
|
||||
#
|
||||
# Testsuite rules
|
||||
#
|
||||
TESTS_PREFIX=tests/
|
||||
|
||||
TESTS_BIN += dtc
|
||||
TESTS_BIN += convert-dtsv0
|
||||
TESTS_BIN += fdtput
|
||||
TESTS_BIN += fdtget
|
||||
TESTS_BIN += fdtdump
|
||||
TESTS_BIN += fdtoverlay
|
||||
|
||||
ifneq ($(MAKECMDGOALS),libfdt)
|
||||
include tests/Makefile.tests
|
||||
endif
|
||||
|
||||
#
|
||||
# Clean rules
|
||||
#
|
||||
STD_CLEANFILES = *~ *.o *.$(SHAREDLIB_EXT) *.d *.a *.i *.s core a.out vgcore.* \
|
||||
*.tab.[ch] *.lex.c *.output
|
||||
|
||||
clean: libfdt_clean tests_clean
|
||||
@$(VECHO) CLEAN
|
||||
rm -f $(STD_CLEANFILES)
|
||||
rm -f $(VERSION_FILE)
|
||||
rm -f $(BIN)
|
||||
rm -f dtc-*.tar dtc-*.tar.sign dtc-*.tar.asc
|
||||
|
||||
#
|
||||
# Generic compile rules
|
||||
#
|
||||
%: %.o
|
||||
@$(VECHO) LD $@
|
||||
$(LINK.c) -o $@ $^ $(LDLIBS_$*)
|
||||
|
||||
%.o: %.c
|
||||
@$(VECHO) CC $@
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
%.o: %.S
|
||||
@$(VECHO) AS $@
|
||||
$(CC) $(CPPFLAGS) $(AFLAGS) -o $@ -c $<
|
||||
clean:
|
||||
rm -f *~ *.o a.out core $(TARGETS)
|
||||
rm -f *.tab.[ch] lex.yy.c
|
||||
rm -f *.i *.output vgcore.*
|
||||
rm -f *.d
|
||||
cd tests && $(MAKE) clean
|
||||
|
||||
%.d: %.c
|
||||
@$(VECHO) DEP $<
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -MM -MG -MT "$*.o $@" $< > $@
|
||||
$(CC) -MM -MG -MT "$*.o $@" $< > $@
|
||||
|
||||
%.d: %.S
|
||||
@$(VECHO) DEP $<
|
||||
$(CC) $(CPPFLAGS) -MM -MG -MT "$*.o $@" $< > $@
|
||||
|
||||
%.i: %.c
|
||||
@$(VECHO) CPP $@
|
||||
$(CC) $(CPPFLAGS) -E $< > $@
|
||||
|
||||
%.s: %.c
|
||||
@$(VECHO) CC -S $@
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -S $<
|
||||
|
||||
%.a:
|
||||
@$(VECHO) AR $@
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
%.lex.c: %.l
|
||||
@$(VECHO) LEX $@
|
||||
$(LEX) -o$@ $<
|
||||
|
||||
%.tab.c %.tab.h: %.y
|
||||
@$(VECHO) BISON $@
|
||||
$(BISON) -b $(basename $(basename $@)) -d $<
|
||||
|
||||
FORCE:
|
||||
|
||||
ifeq ($(MAKE_RESTARTS),10)
|
||||
$(error "Make re-executed itself $(MAKE_RESTARTS) times. Infinite recursion?")
|
||||
endif
|
||||
-include $(DEPFILES)
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# This is not a complete Makefile of itself.
|
||||
# Instead, it is designed to be easily embeddable
|
||||
# into other systems of Makefiles.
|
||||
#
|
||||
|
||||
CONVERT_SRCS = \
|
||||
srcpos.c \
|
||||
util.c
|
||||
|
||||
CONVERT_GEN_SRCS = convert-dtsv0-lexer.lex.c
|
||||
|
||||
CONVERT_OBJS = $(CONVERT_SRCS:%.c=%.o) $(CONVERT_GEN_SRCS:%.c=%.o)
|
||||
23
Makefile.dtc
23
Makefile.dtc
|
|
@ -1,23 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Makefile.dtc
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
DTC_SRCS = \
|
||||
checks.c \
|
||||
data.c \
|
||||
dtc.c \
|
||||
flattree.c \
|
||||
fstree.c \
|
||||
livetree.c \
|
||||
srcpos.c \
|
||||
treesource.c \
|
||||
util.c
|
||||
|
||||
ifneq ($(NO_YAML),1)
|
||||
DTC_SRCS += yamltree.c
|
||||
endif
|
||||
|
||||
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
||||
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
|
||||
FDTDUMP_SRCS = \
|
||||
fdtdump.c \
|
||||
util.c
|
||||
|
||||
FDTDUMP_OBJS = $(FDTDUMP_SRCS:%.c=%.o)
|
||||
|
||||
|
||||
FDTGET_SRCS = \
|
||||
fdtget.c \
|
||||
util.c
|
||||
|
||||
FDTGET_OBJS = $(FDTGET_SRCS:%.c=%.o)
|
||||
|
||||
|
||||
FDTPUT_SRCS = \
|
||||
fdtput.c \
|
||||
util.c
|
||||
|
||||
FDTPUT_OBJS = $(FDTPUT_SRCS:%.c=%.o)
|
||||
|
||||
FDTOVERLAY_SRCS = \
|
||||
fdtoverlay.c \
|
||||
util.c
|
||||
|
||||
FDTOVERLAY_OBJS = $(FDTOVERLAY_SRCS:%.c=%.o)
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
Licensing and contribution policy of dtc and libfdt
|
||||
===================================================
|
||||
|
||||
This dtc package contains two pieces of software: dtc itself, and
|
||||
libfdt which comprises the files in the libfdt/ subdirectory. These
|
||||
two pieces of software, although closely related, are quite distinct.
|
||||
dtc does not incorporate or rely on libfdt for its operation, nor vice
|
||||
versa. It is important that these two pieces of software have
|
||||
different license conditions.
|
||||
|
||||
As SPDX license tags in each source file attest, dtc is licensed
|
||||
under the GNU GPL. The full text of the GPL can be found in the file
|
||||
entitled 'GPL' which should be included in this package. dtc code,
|
||||
therefore, may not be incorporated into works which do not have a GPL
|
||||
compatible license.
|
||||
|
||||
libfdt, however, is GPL/BSD dual-licensed. That is, it may be used
|
||||
either under the terms of the GPL, or under the terms of the 2-clause
|
||||
BSD license (aka the ISC license). The full terms of that license can
|
||||
be found are in the file entitled 'BSD-2-Clause'. This is, in
|
||||
practice, equivalent to being BSD licensed, since the terms of the BSD
|
||||
license are strictly more permissive than the GPL.
|
||||
|
||||
I made the decision to license libfdt in this way because I want to
|
||||
encourage widespread and correct usage of flattened device trees,
|
||||
including by proprietary or otherwise GPL-incompatible firmware or
|
||||
tools. Allowing libfdt to be used under the terms of the BSD license
|
||||
makes that it easier for vendors or authors of such software to do so.
|
||||
|
||||
This does mean that libfdt code could be "stolen" - say, included in a
|
||||
proprietary firmware and extended without contributing those extensions
|
||||
back to the libfdt mainline. While I hope that doesn't happen, I
|
||||
believe the goal of allowing libfdt to be widely used is more
|
||||
important than avoiding that. libfdt is quite small, and hardly
|
||||
rocket science; so the incentive for such impolite behaviour is small,
|
||||
and the inconvenience caused thereby is not dire.
|
||||
|
||||
Licenses such as the LGPL which would allow code to be used in non-GPL
|
||||
software, but also require contributions to be returned were
|
||||
considered. However, libfdt is designed to be used in firmwares and
|
||||
other environments with unusual technical constraints. It's difficult
|
||||
to anticipate all possible changes which might be needed to meld
|
||||
libfdt into such environments and so difficult to suitably word a
|
||||
license that puts the boundary between what is and isn't permitted in
|
||||
the intended place. Again, I judged encouraging widespread use of
|
||||
libfdt by keeping the license terms simple and familiar to be the more
|
||||
important goal.
|
||||
|
||||
**IMPORTANT** It's intended that all of libfdt as released remain
|
||||
permissively licensed this way. Therefore only contributions which
|
||||
are released under these terms can be merged into the libfdt mainline.
|
||||
|
||||
|
||||
David Gibson <david@gibson.dropbear.id.au>
|
||||
(principal original author of dtc and libfdt)
|
||||
2 November 2007
|
||||
100
README.md
100
README.md
|
|
@ -1,100 +0,0 @@
|
|||
# Device Tree Compiler and libfdt
|
||||
|
||||
The source tree contains the Device Tree Compiler (dtc) toolchain for
|
||||
working with device tree source and binary files and also libfdt, a
|
||||
utility library for reading and manipulating the binary format.
|
||||
|
||||
dtc and libfdt are maintained by:
|
||||
|
||||
* [David Gibson `<david@gibson.dropbear.id.au>`](mailto:david@gibson.dropbear.id.au)
|
||||
|
||||
## Python library
|
||||
|
||||
A Python library wrapping libfdt is also available. To build this you
|
||||
will need to install `swig` and Python development files. On Debian
|
||||
distributions:
|
||||
|
||||
```
|
||||
$ sudo apt-get install swig python3-dev
|
||||
```
|
||||
|
||||
The library provides an `Fdt` class which you can use like this:
|
||||
|
||||
```
|
||||
$ PYTHONPATH=../pylibfdt python3
|
||||
>>> import libfdt
|
||||
>>> fdt = libfdt.Fdt(open('test_tree1.dtb', mode='rb').read())
|
||||
>>> node = fdt.path_offset('/subnode@1')
|
||||
>>> print(node)
|
||||
124
|
||||
>>> prop_offset = fdt.first_property_offset(node)
|
||||
>>> prop = fdt.get_property_by_offset(prop_offset)
|
||||
>>> print('%s=%s' % (prop.name, prop.as_str()))
|
||||
compatible=subnode1
|
||||
>>> node2 = fdt.path_offset('/')
|
||||
>>> print(fdt.getprop(node2, 'compatible').as_str())
|
||||
test_tree1
|
||||
```
|
||||
|
||||
You will find tests in `tests/pylibfdt_tests.py` showing how to use each
|
||||
method. Help is available using the Python help command, e.g.:
|
||||
|
||||
```
|
||||
$ cd pylibfdt
|
||||
$ python3 -c "import libfdt; help(libfdt)"
|
||||
```
|
||||
|
||||
If you add new features, please check code coverage:
|
||||
|
||||
```
|
||||
$ sudo apt-get install python3-coverage
|
||||
$ cd tests
|
||||
# It's just 'coverage' on most other distributions
|
||||
$ python3-coverage run pylibfdt_tests.py
|
||||
$ python3-coverage html
|
||||
# Open 'htmlcov/index.html' in your browser
|
||||
```
|
||||
|
||||
The library can be installed with pip from a local source tree:
|
||||
|
||||
```
|
||||
$ pip install . [--user|--prefix=/path/to/install_dir]
|
||||
```
|
||||
|
||||
Or directly from a remote git repo:
|
||||
|
||||
```
|
||||
$ pip install git+git://git.kernel.org/pub/scm/utils/dtc/dtc.git@main
|
||||
```
|
||||
|
||||
The install depends on libfdt shared library being installed on the
|
||||
host system first. Generally, using `--user` or `--prefix` is not
|
||||
necessary and pip will use the default location for the Python
|
||||
installation which varies if the user is root or not.
|
||||
|
||||
You can also install everything via make if you like, but pip is
|
||||
recommended.
|
||||
|
||||
To install both libfdt and pylibfdt you can use:
|
||||
|
||||
```
|
||||
$ make install [PREFIX=/path/to/install_dir]
|
||||
```
|
||||
|
||||
To disable building the python library, even if swig and Python are available,
|
||||
use:
|
||||
|
||||
```
|
||||
$ make NO_PYTHON=1
|
||||
```
|
||||
|
||||
More work remains to support all of libfdt, including access to numeric
|
||||
values.
|
||||
|
||||
## Mailing lists
|
||||
|
||||
* The [devicetree-compiler](mailto:devicetree-compiler@vger.kernel.org)
|
||||
list is for discussion about dtc and libfdt implementation.
|
||||
* Core device tree bindings are discussed on the
|
||||
[devicetree-spec](mailto:devicetree-spec@vger.kernel.org) list.
|
||||
|
||||
10
TODO
10
TODO
|
|
@ -1,8 +1,14 @@
|
|||
- Bugfixes:
|
||||
* Proper handling of boot cpu information
|
||||
- Error handling / reporting
|
||||
* Better categorization of errors into severity levels
|
||||
* Don't stop checking for later errors because of earlier ones
|
||||
whenever possible
|
||||
- Generate mem reserve map
|
||||
* linux,reserve-map property
|
||||
* generating reserve entry for device tree itself
|
||||
* generating reserve entries from tce, rtas etc. properties
|
||||
- Expression support
|
||||
- Macro system
|
||||
|
||||
|
||||
- Testsuite
|
||||
- Actually number releases, all that kind of jazz
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
1.7.2
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
/* regexps for lexing comments are.. tricky. Check if we've actually
|
||||
* got it right */
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
// line comment
|
||||
|
|
@ -25,7 +24,7 @@
|
|||
prop7;
|
||||
/* yet
|
||||
* another
|
||||
* multiline
|
||||
* multline
|
||||
* comment
|
||||
*/
|
||||
prop8;
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005, 2008.
|
||||
*/
|
||||
|
||||
%option noyywrap nounput noinput never-interactive
|
||||
|
||||
%x BYTESTRING
|
||||
%x PROPNODENAME
|
||||
|
||||
PROPNODECHAR [a-zA-Z0-9,._+*#?@-]
|
||||
PATHCHAR ({PROPNODECHAR}|[/])
|
||||
LABEL [a-zA-Z_][a-zA-Z0-9_]*
|
||||
STRING \"([^\\"]|\\.)*\"
|
||||
WS [[:space:]]
|
||||
COMMENT "/*"([^*]|\*+[^*/])*\*+"/"
|
||||
LINECOMMENT "//".*\n
|
||||
GAP ({WS}|{COMMENT}|{LINECOMMENT})*
|
||||
|
||||
%{
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "srcpos.h"
|
||||
#include "util.h"
|
||||
|
||||
static int v1_tagged; /* = 0 */
|
||||
static int cbase = 16;
|
||||
static int saw_hyphen; /* = 0 */
|
||||
static unsigned long long last_val;
|
||||
static char *last_name; /* = NULL */
|
||||
|
||||
static const struct {
|
||||
const char *pattern;
|
||||
int obase, width;
|
||||
} guess_table[] = {
|
||||
{ "*-frequency", 10, 0 },
|
||||
{ "num-*", 10, 0 },
|
||||
{ "#*-cells", 10, 0 },
|
||||
{ "*cache-line-size", 10, 0 },
|
||||
{ "*cache-block-size", 10, 0 },
|
||||
{ "*cache-size", 10, 0 },
|
||||
{ "*cache-sets", 10, 0 },
|
||||
{ "cell-index", 10, 0 },
|
||||
{ "bank-width", 10, 0 },
|
||||
{ "*-fifo-size", 10, 0 },
|
||||
{ "*-frame-size", 10, 0 },
|
||||
{ "*-channel", 10, 0 },
|
||||
{ "current-speed", 10, 0 },
|
||||
{ "phy-map", 16, 8 },
|
||||
{ "dcr-reg", 16, 3 },
|
||||
{ "reg", 16, 8 },
|
||||
{ "ranges", 16, 8},
|
||||
};
|
||||
%}
|
||||
|
||||
%%
|
||||
<*>"/include/"{GAP}{STRING} ECHO;
|
||||
|
||||
<*>\"([^\\"]|\\.)*\" ECHO;
|
||||
|
||||
<*>"/dts-v1/" {
|
||||
die("Input dts file is already version 1\n");
|
||||
}
|
||||
|
||||
<*>"/memreserve/" {
|
||||
if (!v1_tagged) {
|
||||
fprintf(yyout, "/dts-v1/;\n\n");
|
||||
v1_tagged = 1;
|
||||
}
|
||||
|
||||
ECHO;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
<*>{LABEL}: ECHO;
|
||||
|
||||
<INITIAL>[bodh]# {
|
||||
if (*yytext == 'b')
|
||||
cbase = 2;
|
||||
else if (*yytext == 'o')
|
||||
cbase = 8;
|
||||
else if (*yytext == 'd')
|
||||
cbase = 10;
|
||||
else
|
||||
cbase = 16;
|
||||
}
|
||||
|
||||
<INITIAL>[0-9a-fA-F]+ {
|
||||
unsigned long long val;
|
||||
int obase = 16, width = 0;
|
||||
unsigned int i;
|
||||
|
||||
val = strtoull(yytext, NULL, cbase);
|
||||
|
||||
if (saw_hyphen)
|
||||
val = val - last_val + 1;
|
||||
|
||||
if (last_name) {
|
||||
for (i = 0; i < ARRAY_SIZE(guess_table); i++)
|
||||
if (fnmatch(guess_table[i].pattern,
|
||||
last_name, 0) == 0) {
|
||||
obase = guess_table[i].obase;
|
||||
width = guess_table[i].width;
|
||||
}
|
||||
} else {
|
||||
obase = 16;
|
||||
width = 16;
|
||||
}
|
||||
|
||||
if (cbase != 16)
|
||||
obase = cbase;
|
||||
|
||||
switch (obase) {
|
||||
case 2:
|
||||
case 16:
|
||||
fprintf(yyout, "0x%0*llx", width, val);
|
||||
break;
|
||||
case 8:
|
||||
fprintf(yyout, "0%0*llo", width, val);
|
||||
break;
|
||||
case 10:
|
||||
fprintf(yyout, "%*llu", width, val);
|
||||
break;
|
||||
}
|
||||
|
||||
cbase = 16;
|
||||
last_val = val;
|
||||
saw_hyphen = 0;
|
||||
}
|
||||
|
||||
\&{LABEL} ECHO;
|
||||
|
||||
"&{/"{PATHCHAR}+\} ECHO;
|
||||
|
||||
<INITIAL>"&/"{PATHCHAR}+ fprintf(yyout, "&{/%s}", yytext + 2);
|
||||
|
||||
<BYTESTRING>[0-9a-fA-F]{2} ECHO;
|
||||
|
||||
<BYTESTRING>"]" {
|
||||
ECHO;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
<PROPNODENAME>{PROPNODECHAR}+ {
|
||||
ECHO;
|
||||
last_name = xstrdup(yytext);
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
<*>{GAP} ECHO;
|
||||
|
||||
<*>- { /* Hack to convert old style memreserves */
|
||||
saw_hyphen = 1;
|
||||
fprintf(yyout, " ");
|
||||
}
|
||||
|
||||
<*>. {
|
||||
if (!v1_tagged) {
|
||||
fprintf(yyout, "/dts-v1/;\n\n");
|
||||
v1_tagged = 1;
|
||||
}
|
||||
|
||||
ECHO;
|
||||
if (yytext[0] == '[') {
|
||||
BEGIN(BYTESTRING);
|
||||
}
|
||||
if ((yytext[0] == '{')
|
||||
|| (yytext[0] == ';')) {
|
||||
BEGIN(PROPNODENAME);
|
||||
}
|
||||
}
|
||||
|
||||
%%
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] = "convert-dtsv0 [options] <v0 dts file>...";
|
||||
static const char usage_short_opts[] = "" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
USAGE_COMMON_LONG_OPTS
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
USAGE_COMMON_OPTS_HELP
|
||||
};
|
||||
|
||||
static void convert_file(const char *fname)
|
||||
{
|
||||
const char suffix[] = "v1";
|
||||
int len = strlen(fname);
|
||||
char *newname;
|
||||
|
||||
newname = xmalloc(len + sizeof(suffix));
|
||||
memcpy(newname, fname, len);
|
||||
memcpy(newname + len, suffix, sizeof(suffix));
|
||||
|
||||
yyin = fopen(fname, "r");
|
||||
if (!yyin)
|
||||
die("Couldn't open input file %s: %s\n",
|
||||
fname, strerror(errno));
|
||||
|
||||
yyout = fopen(newname, "w");
|
||||
if (!yyout)
|
||||
die("Couldn't open output file %s: %s\n",
|
||||
newname, strerror(errno));
|
||||
|
||||
while(yylex())
|
||||
;
|
||||
|
||||
free(newname);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
int i;
|
||||
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
switch (opt) {
|
||||
case_USAGE_COMMON_FLAGS
|
||||
}
|
||||
}
|
||||
if (argc < 2)
|
||||
usage("missing filename");
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
fprintf(stderr, "Converting %s from dts v0 to dts v1\n", argv[i]);
|
||||
convert_file(argv[i]);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
353
data.c
353
data.c
|
|
@ -1,47 +1,95 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
||||
#if 0
|
||||
static struct data data_init_buf(char *buf, int len)
|
||||
{
|
||||
struct data d;
|
||||
|
||||
d.asize = 0;
|
||||
d.len = len;
|
||||
d.val = buf;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_ref_string(char *str)
|
||||
{
|
||||
return data_init_buf(str, strlen(str)+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void fixup_free(struct fixup *f)
|
||||
{
|
||||
free(f->ref);
|
||||
free(f);
|
||||
}
|
||||
|
||||
void data_free(struct data d)
|
||||
{
|
||||
struct marker *m, *nm;
|
||||
struct fixup *f;
|
||||
|
||||
m = d.markers;
|
||||
while (m) {
|
||||
nm = m->next;
|
||||
free(m->ref);
|
||||
free(m);
|
||||
m = nm;
|
||||
f = d.refs;
|
||||
while (f) {
|
||||
struct fixup *nf;
|
||||
|
||||
nf = f->next;
|
||||
fixup_free(f);
|
||||
f = nf;
|
||||
}
|
||||
|
||||
assert(!d.val || d.asize);
|
||||
|
||||
if (d.val)
|
||||
free(d.val);
|
||||
}
|
||||
|
||||
struct data data_grow_for(struct data d, unsigned int xlen)
|
||||
struct data data_grow_for(struct data d, int xlen)
|
||||
{
|
||||
struct data nd;
|
||||
unsigned int newsize;
|
||||
int newsize;
|
||||
|
||||
/* we must start with an allocated datum */
|
||||
assert(!d.val || d.asize);
|
||||
|
||||
if (xlen == 0)
|
||||
return d;
|
||||
|
||||
nd = d;
|
||||
|
||||
newsize = xlen;
|
||||
|
||||
while ((d.len + xlen) > newsize)
|
||||
newsize *= 2;
|
||||
|
||||
nd.asize = newsize;
|
||||
nd.val = xrealloc(d.val, newsize);
|
||||
nd.len = d.len;
|
||||
nd.refs = d.refs;
|
||||
|
||||
assert(nd.asize >= (d.len + xlen));
|
||||
|
||||
return nd;
|
||||
}
|
||||
|
||||
struct data data_copy_mem(const char *mem, int len)
|
||||
struct data data_copy_mem(char *mem, int len)
|
||||
{
|
||||
struct data d;
|
||||
|
||||
|
|
@ -53,58 +101,113 @@ struct data data_copy_mem(const char *mem, int len)
|
|||
return d;
|
||||
}
|
||||
|
||||
struct data data_copy_escape_string(const char *s, int len)
|
||||
static char get_oct_char(char *s, int *i)
|
||||
{
|
||||
char x[4];
|
||||
char *endx;
|
||||
long val;
|
||||
|
||||
x[3] = '\0';
|
||||
x[0] = s[(*i)];
|
||||
if (x[0]) {
|
||||
x[1] = s[(*i)+1];
|
||||
if (x[1])
|
||||
x[2] = s[(*i)+2];
|
||||
}
|
||||
|
||||
val = strtol(x, &endx, 8);
|
||||
if ((endx - x) == 0)
|
||||
fprintf(stderr, "Empty \\nnn escape\n");
|
||||
|
||||
(*i) += endx - x;
|
||||
return val;
|
||||
}
|
||||
|
||||
static char get_hex_char(char *s, int *i)
|
||||
{
|
||||
char x[3];
|
||||
char *endx;
|
||||
long val;
|
||||
|
||||
x[2] = '\0';
|
||||
x[0] = s[(*i)];
|
||||
if (x[0])
|
||||
x[1] = s[(*i)+1];
|
||||
|
||||
val = strtol(x, &endx, 16);
|
||||
if ((endx - x) == 0)
|
||||
fprintf(stderr, "Empty \\x escape\n");
|
||||
|
||||
(*i) += endx - x;
|
||||
return val;
|
||||
}
|
||||
|
||||
struct data data_copy_escape_string(char *s, int len)
|
||||
{
|
||||
int i = 0;
|
||||
struct data d;
|
||||
char *q;
|
||||
|
||||
d = data_add_marker(empty_data, TYPE_STRING, NULL);
|
||||
d = data_grow_for(d, len + 1);
|
||||
d = data_grow_for(empty_data, strlen(s)+1);
|
||||
|
||||
q = d.val;
|
||||
while (i < len) {
|
||||
char c = s[i++];
|
||||
|
||||
if (c == '\\')
|
||||
c = get_escape_char(s, &i);
|
||||
if (c != '\\') {
|
||||
q[d.len++] = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
q[d.len++] = c;
|
||||
c = s[i++];
|
||||
assert(c);
|
||||
switch (c) {
|
||||
case 't':
|
||||
q[d.len++] = '\t';
|
||||
break;
|
||||
case 'n':
|
||||
q[d.len++] = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
q[d.len++] = '\r';
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
i--; /* need to re-read the first digit as
|
||||
* part of the octal value */
|
||||
q[d.len++] = get_oct_char(s, &i);
|
||||
break;
|
||||
case 'x':
|
||||
q[d.len++] = get_hex_char(s, &i);
|
||||
break;
|
||||
default:
|
||||
q[d.len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
q[d.len++] = '\0';
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_copy_file(FILE *f, size_t maxlen)
|
||||
struct data data_copy_file(FILE *f, size_t len)
|
||||
{
|
||||
struct data d = empty_data;
|
||||
struct data d;
|
||||
|
||||
d = data_add_marker(d, TYPE_NONE, NULL);
|
||||
while (!feof(f) && (d.len < maxlen)) {
|
||||
size_t chunksize, ret;
|
||||
d = data_grow_for(empty_data, len);
|
||||
|
||||
if (maxlen == (size_t)-1)
|
||||
chunksize = 4096;
|
||||
else
|
||||
chunksize = maxlen - d.len;
|
||||
|
||||
d = data_grow_for(d, chunksize);
|
||||
ret = fread(d.val + d.len, 1, chunksize, f);
|
||||
|
||||
if (ferror(f))
|
||||
die("Error reading file into data: %s", strerror(errno));
|
||||
|
||||
if (d.len + ret < d.len)
|
||||
die("Overflow reading file into data\n");
|
||||
|
||||
d.len += ret;
|
||||
}
|
||||
d.len = len;
|
||||
fread(d.val, len, 1, f);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_append_data(struct data d, const void *p, int len)
|
||||
struct data data_append_data(struct data d, void *p, int len)
|
||||
{
|
||||
d = data_grow_for(d, len);
|
||||
memcpy(d.val + d.len, p, len);
|
||||
|
|
@ -112,96 +215,28 @@ struct data data_append_data(struct data d, const void *p, int len)
|
|||
return d;
|
||||
}
|
||||
|
||||
struct data data_insert_at_marker(struct data d, struct marker *m,
|
||||
const void *p, int len)
|
||||
{
|
||||
d = data_grow_for(d, len);
|
||||
memmove(d.val + m->offset + len, d.val + m->offset, d.len - m->offset);
|
||||
memcpy(d.val + m->offset, p, len);
|
||||
d.len += len;
|
||||
|
||||
/* Adjust all markers after the one we're inserting at */
|
||||
m = m->next;
|
||||
for_each_marker(m)
|
||||
m->offset += len;
|
||||
return d;
|
||||
}
|
||||
|
||||
static struct data data_append_markers(struct data d, struct marker *m)
|
||||
{
|
||||
struct marker **mp = &d.markers;
|
||||
|
||||
/* Find the end of the markerlist */
|
||||
while (*mp)
|
||||
mp = &((*mp)->next);
|
||||
*mp = m;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_merge(struct data d1, struct data d2)
|
||||
{
|
||||
struct data d;
|
||||
struct marker *m2 = d2.markers;
|
||||
|
||||
d = data_append_markers(data_append_data(d1, d2.val, d2.len), m2);
|
||||
|
||||
/* Adjust for the length of d1 */
|
||||
for_each_marker(m2)
|
||||
m2->offset += d1.len;
|
||||
|
||||
d2.markers = NULL; /* So data_free() doesn't clobber them */
|
||||
data_free(d2);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_append_integer(struct data d, uint64_t value, int bits)
|
||||
{
|
||||
uint8_t value_8;
|
||||
fdt16_t value_16;
|
||||
fdt32_t value_32;
|
||||
fdt64_t value_64;
|
||||
|
||||
switch (bits) {
|
||||
case 8:
|
||||
value_8 = value;
|
||||
return data_append_data(d, &value_8, 1);
|
||||
|
||||
case 16:
|
||||
value_16 = cpu_to_fdt16(value);
|
||||
return data_append_data(d, &value_16, 2);
|
||||
|
||||
case 32:
|
||||
value_32 = cpu_to_fdt32(value);
|
||||
return data_append_data(d, &value_32, 4);
|
||||
|
||||
case 64:
|
||||
value_64 = cpu_to_fdt64(value);
|
||||
return data_append_data(d, &value_64, 8);
|
||||
|
||||
default:
|
||||
die("Invalid literal size (%d)\n", bits);
|
||||
}
|
||||
}
|
||||
|
||||
struct data data_append_re(struct data d, uint64_t address, uint64_t size)
|
||||
{
|
||||
struct fdt_reserve_entry re;
|
||||
|
||||
re.address = cpu_to_fdt64(address);
|
||||
re.size = cpu_to_fdt64(size);
|
||||
|
||||
return data_append_data(d, &re, sizeof(re));
|
||||
}
|
||||
|
||||
struct data data_append_cell(struct data d, cell_t word)
|
||||
{
|
||||
return data_append_integer(d, word, sizeof(word) * 8);
|
||||
cell_t beword = cpu_to_be32(word);
|
||||
|
||||
return data_append_data(d, &beword, sizeof(beword));
|
||||
}
|
||||
|
||||
struct data data_append_addr(struct data d, uint64_t addr)
|
||||
struct data data_append_re(struct data d, struct reserve_entry *re)
|
||||
{
|
||||
return data_append_integer(d, addr, sizeof(addr) * 8);
|
||||
struct reserve_entry bere;
|
||||
|
||||
bere.address = cpu_to_be64(re->address);
|
||||
bere.size = cpu_to_be64(re->size);
|
||||
|
||||
return data_append_data(d, &bere, sizeof(bere));
|
||||
}
|
||||
|
||||
struct data data_append_addr(struct data d, u64 addr)
|
||||
{
|
||||
u64 beaddr = cpu_to_be64(addr);
|
||||
|
||||
return data_append_data(d, &beaddr, sizeof(beaddr));
|
||||
}
|
||||
|
||||
struct data data_append_byte(struct data d, uint8_t byte)
|
||||
|
|
@ -224,70 +259,36 @@ struct data data_append_align(struct data d, int align)
|
|||
return data_append_zeroes(d, newlen - d.len);
|
||||
}
|
||||
|
||||
struct data data_add_marker(struct data d, enum markertype type, char *ref)
|
||||
struct data data_add_fixup(struct data d, char *ref)
|
||||
{
|
||||
struct marker *m;
|
||||
struct fixup *f;
|
||||
struct data nd;
|
||||
|
||||
m = alloc_marker(d.len, type, ref);
|
||||
f = xmalloc(sizeof(*f));
|
||||
f->offset = d.len;
|
||||
f->ref = ref;
|
||||
f->next = d.refs;
|
||||
|
||||
return data_append_markers(d, m);
|
||||
nd = d;
|
||||
nd.refs = f;
|
||||
|
||||
return nd;
|
||||
}
|
||||
|
||||
bool data_is_one_string(struct data d)
|
||||
int data_is_one_string(struct data d)
|
||||
{
|
||||
int i;
|
||||
int len = d.len;
|
||||
|
||||
if (len == 0)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len-1; i++)
|
||||
if (d.val[i] == '\0')
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
if (d.val[len-1] != '\0')
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct data data_insert_data(struct data d, struct marker *m, struct data old)
|
||||
{
|
||||
unsigned int offset = m->offset;
|
||||
struct marker *next = m->next;
|
||||
struct marker *marker;
|
||||
struct data new_data;
|
||||
char *ref;
|
||||
|
||||
new_data = data_insert_at_marker(d, m, old.val, old.len);
|
||||
|
||||
/* Copy all markers from old value */
|
||||
marker = old.markers;
|
||||
for_each_marker(marker) {
|
||||
ref = NULL;
|
||||
|
||||
if (marker->ref)
|
||||
ref = xstrdup(marker->ref);
|
||||
|
||||
m->next = alloc_marker(marker->offset + offset, marker->type,
|
||||
ref);
|
||||
m = m->next;
|
||||
}
|
||||
m->next = next;
|
||||
|
||||
return new_data;
|
||||
}
|
||||
|
||||
struct marker *alloc_marker(unsigned int offset, enum markertype type,
|
||||
char *ref)
|
||||
{
|
||||
struct marker *m;
|
||||
|
||||
m = xmalloc(sizeof(*m));
|
||||
m->offset = offset;
|
||||
m->type = type;
|
||||
m->ref = ref;
|
||||
m->next = NULL;
|
||||
|
||||
return m;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
348
dtc-lexer.l
348
dtc-lexer.l
|
|
@ -1,36 +1,40 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
%option noyywrap nounput noinput never-interactive
|
||||
%option noyywrap nounput yylineno
|
||||
|
||||
%x CELLDATA
|
||||
%x BYTESTRING
|
||||
%x PROPNODENAME
|
||||
%s V1
|
||||
%x MEMRESERVE
|
||||
|
||||
PROPNODECHAR [a-zA-Z0-9,._+*#?@-]
|
||||
PATHCHAR ({PROPNODECHAR}|[/])
|
||||
LABEL [a-zA-Z_][a-zA-Z0-9_]*
|
||||
STRING \"([^\\"]|\\.)*\"
|
||||
CHAR_LITERAL '([^']|\\')*'
|
||||
WS [[:space:]]
|
||||
COMMENT "/*"([^*]|\*+[^*/])*\*+"/"
|
||||
LINECOMMENT "//".*\n
|
||||
PROPCHAR [a-zA-Z0-9,._+*#?-]
|
||||
UNITCHAR [0-9a-f,]
|
||||
WS [ \t\n]
|
||||
|
||||
REFCHAR ({PROPCHAR}|{UNITCHAR}|[/@])
|
||||
|
||||
%{
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
#include "dtc-parser.tab.h"
|
||||
|
||||
extern bool treesource_error;
|
||||
|
||||
/* CAUTION: this will stop working if we ever use yyless() or yyunput() */
|
||||
#define YY_USER_ACTION \
|
||||
{ \
|
||||
srcpos_update(&yylloc, yytext, yyleng); \
|
||||
}
|
||||
|
||||
/*#define LEXDEBUG 1*/
|
||||
|
||||
#ifdef LEXDEBUG
|
||||
|
|
@ -39,274 +43,138 @@ extern bool treesource_error;
|
|||
#define DPRINT(fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
static int dts_version = 1;
|
||||
|
||||
#define BEGIN_DEFAULT() DPRINT("<V1>\n"); \
|
||||
BEGIN(V1); \
|
||||
|
||||
static void push_input_file(const char *filename);
|
||||
static bool pop_input_file(void);
|
||||
static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
<*>"/include/"{WS}*{STRING} {
|
||||
char *name = strchr(yytext, '\"') + 1;
|
||||
yytext[yyleng-1] = '\0';
|
||||
push_input_file(name);
|
||||
}
|
||||
|
||||
<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)* {
|
||||
char *line, *fnstart, *fnend;
|
||||
struct data fn;
|
||||
/* skip text before line # */
|
||||
line = yytext;
|
||||
while (!isdigit((unsigned char)*line))
|
||||
line++;
|
||||
|
||||
/* regexp ensures that first and list "
|
||||
* in the whole yytext are those at
|
||||
* beginning and end of the filename string */
|
||||
fnstart = memchr(yytext, '"', yyleng);
|
||||
for (fnend = yytext + yyleng - 1;
|
||||
*fnend != '"'; fnend--)
|
||||
;
|
||||
assert(fnstart && fnend && (fnend > fnstart));
|
||||
|
||||
fn = data_copy_escape_string(fnstart + 1,
|
||||
fnend - fnstart - 1);
|
||||
|
||||
/* Don't allow nuls in filenames */
|
||||
if (memchr(fn.val, '\0', fn.len - 1))
|
||||
lexical_error("nul in line number directive");
|
||||
|
||||
/* -1 since #line is the number of the next line */
|
||||
srcpos_set_line(xstrdup(fn.val), atoi(line) - 1);
|
||||
data_free(fn);
|
||||
}
|
||||
|
||||
<*><<EOF>> {
|
||||
if (!pop_input_file()) {
|
||||
yyterminate();
|
||||
}
|
||||
}
|
||||
|
||||
<*>{STRING} {
|
||||
\"[^"]*\" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("String: %s\n", yytext);
|
||||
yylval.data = data_copy_escape_string(yytext+1,
|
||||
yyleng-2);
|
||||
yylloc.first_line = yylineno;
|
||||
return DT_STRING;
|
||||
}
|
||||
|
||||
<*>"/dts-v1/" {
|
||||
DPRINT("Keyword: /dts-v1/\n");
|
||||
dts_version = 1;
|
||||
BEGIN_DEFAULT();
|
||||
return DT_V1;
|
||||
}
|
||||
|
||||
<*>"/plugin/" {
|
||||
DPRINT("Keyword: /plugin/\n");
|
||||
return DT_PLUGIN;
|
||||
}
|
||||
|
||||
<*>"/memreserve/" {
|
||||
"/memreserve/" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Keyword: /memreserve/\n");
|
||||
BEGIN_DEFAULT();
|
||||
BEGIN(MEMRESERVE);
|
||||
return DT_MEMRESERVE;
|
||||
}
|
||||
|
||||
<*>"/bits/" {
|
||||
DPRINT("Keyword: /bits/\n");
|
||||
BEGIN_DEFAULT();
|
||||
return DT_BITS;
|
||||
}
|
||||
|
||||
<*>"/delete-property/" {
|
||||
DPRINT("Keyword: /delete-property/\n");
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
return DT_DEL_PROP;
|
||||
}
|
||||
|
||||
<*>"/delete-node/" {
|
||||
DPRINT("Keyword: /delete-node/\n");
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
return DT_DEL_NODE;
|
||||
}
|
||||
|
||||
<*>"/omit-if-no-ref/" {
|
||||
DPRINT("Keyword: /omit-if-no-ref/\n");
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
return DT_OMIT_NO_REF;
|
||||
}
|
||||
|
||||
<*>{LABEL}: {
|
||||
DPRINT("Label: %s\n", yytext);
|
||||
yylval.labelref = xstrdup(yytext);
|
||||
yylval.labelref[yyleng-1] = '\0';
|
||||
return DT_LABEL;
|
||||
}
|
||||
|
||||
<V1>{LABEL} {
|
||||
/* Missed includes or macro definitions while
|
||||
* preprocessing can lead to unexpected identifiers in
|
||||
* the input. Report a slightly more informative error
|
||||
* in this case */
|
||||
|
||||
lexical_error("Unexpected '%s'", yytext);
|
||||
|
||||
/* Treat it as a literal which often generates further
|
||||
* useful error messages */
|
||||
|
||||
yylval.integer = 0;
|
||||
return DT_LITERAL;
|
||||
}
|
||||
|
||||
<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
|
||||
char *e;
|
||||
DPRINT("Integer Literal: '%s'\n", yytext);
|
||||
|
||||
errno = 0;
|
||||
yylval.integer = strtoull(yytext, &e, 0);
|
||||
|
||||
if (*e && e[strspn(e, "UL")]) {
|
||||
lexical_error("Bad integer literal '%s'",
|
||||
yytext);
|
||||
<MEMRESERVE>[0-9a-fA-F]+ {
|
||||
yylloc.first_line = yylineno;
|
||||
if (yyleng > 2*sizeof(yylval.addr)) {
|
||||
fprintf(stderr, "Address value %s too large\n",
|
||||
yytext);
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
lexical_error("Integer literal '%s' out of range",
|
||||
yytext);
|
||||
else
|
||||
/* ERANGE is the only strtoull error triggerable
|
||||
* by strings matching the pattern */
|
||||
assert(errno == 0);
|
||||
return DT_LITERAL;
|
||||
yylval.addr = (u64) strtoull(yytext, NULL, 16);
|
||||
DPRINT("Addr: %llx\n",
|
||||
(unsigned long long)yylval.addr);
|
||||
return DT_ADDR;
|
||||
}
|
||||
|
||||
<*>{CHAR_LITERAL} {
|
||||
struct data d;
|
||||
DPRINT("Character literal: %s\n", yytext);
|
||||
<MEMRESERVE>";" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("/MEMRESERVE\n");
|
||||
BEGIN(INITIAL);
|
||||
return ';';
|
||||
}
|
||||
|
||||
d = data_copy_escape_string(yytext+1, yyleng-2);
|
||||
if (d.len == 1) {
|
||||
lexical_error("Empty character literal");
|
||||
yylval.integer = 0;
|
||||
} else {
|
||||
yylval.integer = (unsigned char)d.val[0];
|
||||
|
||||
if (d.len > 2)
|
||||
lexical_error("Character literal has %d"
|
||||
" characters instead of 1",
|
||||
d.len - 1);
|
||||
<CELLDATA>[0-9a-fA-F]+ {
|
||||
yylloc.first_line = yylineno;
|
||||
if (yyleng > 2*sizeof(yylval.cval)) {
|
||||
fprintf(stderr,
|
||||
"Cell value %s too long\n", yytext);
|
||||
}
|
||||
|
||||
data_free(d);
|
||||
return DT_CHAR_LITERAL;
|
||||
yylval.cval = strtoul(yytext, NULL, 16);
|
||||
DPRINT("Cell: %x\n", yylval.cval);
|
||||
return DT_CELL;
|
||||
}
|
||||
|
||||
<*>\&{LABEL} { /* label reference */
|
||||
<CELLDATA>">" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("/CELLDATA\n");
|
||||
BEGIN(INITIAL);
|
||||
return '>';
|
||||
}
|
||||
|
||||
<CELLDATA>\&{REFCHAR}* {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Ref: %s\n", yytext+1);
|
||||
yylval.labelref = xstrdup(yytext+1);
|
||||
return DT_LABEL_REF;
|
||||
}
|
||||
|
||||
<*>"&{"{PATHCHAR}*\} { /* new-style path reference */
|
||||
yytext[yyleng-1] = '\0';
|
||||
DPRINT("Ref: %s\n", yytext+2);
|
||||
yylval.labelref = xstrdup(yytext+2);
|
||||
return DT_PATH_REF;
|
||||
yylval.str = strdup(yytext+1);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<BYTESTRING>[0-9a-fA-F]{2} {
|
||||
yylloc.first_line = yylineno;
|
||||
yylval.byte = strtol(yytext, NULL, 16);
|
||||
DPRINT("Byte: %02x\n", (int)yylval.byte);
|
||||
return DT_BYTE;
|
||||
}
|
||||
|
||||
<BYTESTRING>"]" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("/BYTESTRING\n");
|
||||
BEGIN_DEFAULT();
|
||||
BEGIN(INITIAL);
|
||||
return ']';
|
||||
}
|
||||
|
||||
<PROPNODENAME>\\?{PROPNODECHAR}+ {
|
||||
DPRINT("PropNodeName: %s\n", yytext);
|
||||
yylval.propnodename = xstrdup((yytext[0] == '\\') ?
|
||||
yytext + 1 : yytext);
|
||||
BEGIN_DEFAULT();
|
||||
return DT_PROPNODENAME;
|
||||
{PROPCHAR}+ {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("PropName: %s\n", yytext);
|
||||
yylval.str = strdup(yytext);
|
||||
return DT_PROPNAME;
|
||||
}
|
||||
|
||||
"/incbin/" {
|
||||
DPRINT("Binary Include\n");
|
||||
return DT_INCBIN;
|
||||
{PROPCHAR}+(@{UNITCHAR}+)? {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("NodeName: %s\n", yytext);
|
||||
yylval.str = strdup(yytext);
|
||||
return DT_NODENAME;
|
||||
}
|
||||
|
||||
|
||||
[a-zA-Z_][a-zA-Z0-9_]*: {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Label: %s\n", yytext);
|
||||
yylval.str = strdup(yytext);
|
||||
yylval.str[yyleng-1] = '\0';
|
||||
return DT_LABEL;
|
||||
}
|
||||
|
||||
<*>{WS}+ /* eat whitespace */
|
||||
<*>{COMMENT}+ /* eat C-style comments */
|
||||
<*>{LINECOMMENT}+ /* eat C++-style comments */
|
||||
|
||||
<*>"<<" { return DT_LSHIFT; };
|
||||
<*>">>" { return DT_RSHIFT; };
|
||||
<*>"<=" { return DT_LE; };
|
||||
<*>">=" { return DT_GE; };
|
||||
<*>"==" { return DT_EQ; };
|
||||
<*>"!=" { return DT_NE; };
|
||||
<*>"&&" { return DT_AND; };
|
||||
<*>"||" { return DT_OR; };
|
||||
<*>"/*"([^*]|\*+[^*/])*\*+"/" {
|
||||
yylloc.first_line = yylineno;
|
||||
DPRINT("Comment: %s\n", yytext);
|
||||
/* eat comments */
|
||||
}
|
||||
|
||||
<*>"//".*\n /* eat line comments */
|
||||
|
||||
<*>. {
|
||||
yylloc.first_line = yylineno;
|
||||
switch (yytext[0]) {
|
||||
case '<':
|
||||
DPRINT("CELLDATA\n");
|
||||
BEGIN(CELLDATA);
|
||||
break;
|
||||
case '[':
|
||||
DPRINT("BYTESTRING\n");
|
||||
BEGIN(BYTESTRING);
|
||||
break;
|
||||
default:
|
||||
|
||||
DPRINT("Char: %c (\\x%02x)\n", yytext[0],
|
||||
(unsigned)yytext[0]);
|
||||
if (yytext[0] == '[') {
|
||||
DPRINT("<BYTESTRING>\n");
|
||||
BEGIN(BYTESTRING);
|
||||
}
|
||||
if ((yytext[0] == '{')
|
||||
|| (yytext[0] == ';')) {
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
break;
|
||||
}
|
||||
|
||||
return yytext[0];
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
static void push_input_file(const char *filename)
|
||||
{
|
||||
assert(filename);
|
||||
|
||||
srcfile_push(filename);
|
||||
|
||||
yyin = current_srcfile->f;
|
||||
|
||||
yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
|
||||
static bool pop_input_file(void)
|
||||
{
|
||||
if (srcfile_pop() == 0)
|
||||
return false;
|
||||
|
||||
yypop_buffer_state();
|
||||
yyin = current_srcfile->f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lexical_error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
srcpos_verror(&yylloc, "Lexical error", fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
treesource_error = true;
|
||||
}
|
||||
|
|
|
|||
625
dtc-parser.y
625
dtc-parser.y
|
|
@ -1,599 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
%glr-parser
|
||||
%locations
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
extern int yylex(void);
|
||||
extern void yyerror(char const *s);
|
||||
#define ERROR(loc, ...) \
|
||||
do { \
|
||||
srcpos_error((loc), "Error", __VA_ARGS__); \
|
||||
treesource_error = true; \
|
||||
} while (0)
|
||||
int yylex (void);
|
||||
void yyerror (char const *);
|
||||
|
||||
#define YYERROR_CALL(msg) yyerror(msg)
|
||||
|
||||
extern struct dt_info *parser_output;
|
||||
extern bool treesource_error;
|
||||
|
||||
static bool is_ref_relative(const char *ref)
|
||||
{
|
||||
return ref[0] != '/' && strchr(&ref[1], '/');
|
||||
}
|
||||
extern struct boot_info *the_boot_info;
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
char *propnodename;
|
||||
char *labelref;
|
||||
uint8_t byte;
|
||||
cell_t cval;
|
||||
u8 byte;
|
||||
char *str;
|
||||
struct data data;
|
||||
|
||||
struct {
|
||||
struct data data;
|
||||
int bits;
|
||||
} array;
|
||||
|
||||
struct property *prop;
|
||||
struct property *proplist;
|
||||
struct node *node;
|
||||
struct node *nodelist;
|
||||
int datalen;
|
||||
int hexlen;
|
||||
u64 addr;
|
||||
struct reserve_info *re;
|
||||
uint64_t integer;
|
||||
unsigned int flags;
|
||||
}
|
||||
|
||||
%token DT_V1
|
||||
%token DT_PLUGIN
|
||||
%token DT_MEMRESERVE
|
||||
%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
|
||||
%token DT_BITS
|
||||
%token DT_DEL_PROP
|
||||
%token DT_DEL_NODE
|
||||
%token DT_OMIT_NO_REF
|
||||
%token <propnodename> DT_PROPNODENAME
|
||||
%token <integer> DT_LITERAL
|
||||
%token <integer> DT_CHAR_LITERAL
|
||||
%token <addr> DT_ADDR
|
||||
%token <str> DT_PROPNAME
|
||||
%token <str> DT_NODENAME
|
||||
%token <cval> DT_CELL
|
||||
%token <byte> DT_BYTE
|
||||
%token <data> DT_STRING
|
||||
%token <labelref> DT_LABEL
|
||||
%token <labelref> DT_LABEL_REF
|
||||
%token <labelref> DT_PATH_REF
|
||||
%token DT_INCBIN
|
||||
%token <str> DT_UNIT
|
||||
%token <str> DT_LABEL
|
||||
%token <str> DT_REF
|
||||
|
||||
%type <data> propdata
|
||||
%type <data> propdataprefix
|
||||
%type <flags> header
|
||||
%type <flags> headers
|
||||
%type <re> memreserve
|
||||
%type <re> memreserves
|
||||
%type <array> arrayprefix
|
||||
%type <data> celllist
|
||||
%type <data> bytestring
|
||||
%type <prop> propdef
|
||||
%type <proplist> proplist
|
||||
%type <labelref> dt_ref
|
||||
|
||||
%type <node> devicetree
|
||||
%type <node> nodedef
|
||||
%type <node> subnode
|
||||
%type <nodelist> subnodes
|
||||
|
||||
%type <integer> integer_prim
|
||||
%type <integer> integer_unary
|
||||
%type <integer> integer_mul
|
||||
%type <integer> integer_add
|
||||
%type <integer> integer_shift
|
||||
%type <integer> integer_rela
|
||||
%type <integer> integer_eq
|
||||
%type <integer> integer_bitand
|
||||
%type <integer> integer_bitxor
|
||||
%type <integer> integer_bitor
|
||||
%type <integer> integer_and
|
||||
%type <integer> integer_or
|
||||
%type <integer> integer_trinary
|
||||
%type <integer> integer_expr
|
||||
%type <str> label
|
||||
%type <str> nodename
|
||||
|
||||
%%
|
||||
|
||||
sourcefile:
|
||||
headers memreserves devicetree
|
||||
{
|
||||
parser_output = build_dt_info($1, $2, $3,
|
||||
guess_boot_cpuid($3));
|
||||
sourcefile: memreserves devicetree {
|
||||
the_boot_info = build_boot_info($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
header:
|
||||
DT_V1 ';'
|
||||
{
|
||||
$$ = DTSF_V1;
|
||||
}
|
||||
| DT_V1 ';' DT_PLUGIN ';'
|
||||
{
|
||||
$$ = DTSF_V1 | DTSF_PLUGIN;
|
||||
}
|
||||
;
|
||||
|
||||
headers:
|
||||
header
|
||||
| header headers
|
||||
{
|
||||
if ($2 != $1)
|
||||
ERROR(&@2, "Header flags don't match earlier ones");
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
memreserves:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| memreserve memreserves
|
||||
{
|
||||
memreserves: memreserve memreserves {
|
||||
$$ = chain_reserve_entry($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
memreserve:
|
||||
DT_MEMRESERVE integer_prim integer_prim ';'
|
||||
{
|
||||
$$ = build_reserve_entry($2, $3);
|
||||
}
|
||||
| DT_LABEL memreserve
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
dt_ref: DT_LABEL_REF | DT_PATH_REF;
|
||||
|
||||
devicetree:
|
||||
'/' nodedef
|
||||
{
|
||||
$$ = name_node($2, "");
|
||||
}
|
||||
| devicetree '/' nodedef
|
||||
{
|
||||
$$ = merge_nodes($1, $3);
|
||||
}
|
||||
| dt_ref nodedef
|
||||
{
|
||||
/*
|
||||
* We rely on the rule being always:
|
||||
* versioninfo plugindecl memreserves devicetree
|
||||
* so $-1 is what we want (plugindecl)
|
||||
*/
|
||||
if (!($<flags>-1 & DTSF_PLUGIN))
|
||||
ERROR(&@2, "Label or path %s not found", $1);
|
||||
else if (is_ref_relative($1))
|
||||
ERROR(&@2, "Label-relative reference %s not supported in plugin", $1);
|
||||
$$ = add_orphan_node(
|
||||
name_node(build_node(NULL, NULL, NULL),
|
||||
""),
|
||||
$2, $1);
|
||||
}
|
||||
| devicetree DT_LABEL dt_ref nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (($<flags>-1 & DTSF_PLUGIN) && is_ref_relative($3))
|
||||
ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
|
||||
|
||||
if (target) {
|
||||
add_label(&target->labels, $2);
|
||||
merge_nodes(target, $4);
|
||||
} else
|
||||
ERROR(&@3, "Label or path %s not found", $3);
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_PATH_REF nodedef
|
||||
{
|
||||
/*
|
||||
* We rely on the rule being always:
|
||||
* versioninfo plugindecl memreserves devicetree
|
||||
* so $-1 is what we want (plugindecl)
|
||||
*/
|
||||
if ($<flags>-1 & DTSF_PLUGIN) {
|
||||
if (is_ref_relative($2))
|
||||
ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
|
||||
add_orphan_node($1, $3, $2);
|
||||
} else {
|
||||
struct node *target = get_node_by_ref($1, $2);
|
||||
|
||||
if (target)
|
||||
merge_nodes(target, $3);
|
||||
else
|
||||
ERROR(&@2, "Label or path %s not found", $2);
|
||||
}
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_LABEL_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $2);
|
||||
|
||||
if (target) {
|
||||
merge_nodes(target, $3);
|
||||
} else {
|
||||
/*
|
||||
* We rely on the rule being always:
|
||||
* versioninfo plugindecl memreserves devicetree
|
||||
* so $-1 is what we want (plugindecl)
|
||||
*/
|
||||
if ($<flags>-1 & DTSF_PLUGIN)
|
||||
add_orphan_node($1, $3, $2);
|
||||
else
|
||||
ERROR(&@2, "Label or path %s not found", $2);
|
||||
}
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_DEL_NODE dt_ref ';'
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (target)
|
||||
delete_node(target);
|
||||
else
|
||||
ERROR(&@3, "Label or path %s not found", $3);
|
||||
|
||||
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_OMIT_NO_REF dt_ref ';'
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (target)
|
||||
omit_node_if_unused(target);
|
||||
else
|
||||
ERROR(&@3, "Label or path %s not found", $3);
|
||||
|
||||
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
nodedef:
|
||||
'{' proplist subnodes '}' ';'
|
||||
{
|
||||
$$ = build_node($2, $3, &@$);
|
||||
}
|
||||
;
|
||||
|
||||
proplist:
|
||||
/* empty */
|
||||
{
|
||||
| /* empty */ {
|
||||
$$ = NULL;
|
||||
}
|
||||
| proplist propdef
|
||||
{
|
||||
$$ = chain_property($2, $1);
|
||||
;
|
||||
|
||||
memreserve: DT_MEMRESERVE DT_ADDR DT_ADDR ';' {
|
||||
$$ = build_reserve_entry($2, $3, NULL);
|
||||
}
|
||||
| DT_MEMRESERVE DT_ADDR '-' DT_ADDR ';' {
|
||||
$$ = build_reserve_entry($2, $4 - $2 + 1, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
propdef:
|
||||
DT_PROPNODENAME '=' propdata ';'
|
||||
{
|
||||
$$ = build_property($1, $3, &@$);
|
||||
free($1);
|
||||
}
|
||||
| DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = build_property($1, empty_data, &@$);
|
||||
free($1);
|
||||
}
|
||||
| DT_DEL_PROP DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = build_property_delete($2);
|
||||
free($2);
|
||||
}
|
||||
| DT_LABEL propdef
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
devicetree: '/' nodedef {
|
||||
$$ = name_node($2, "", NULL);
|
||||
}
|
||||
;
|
||||
|
||||
propdata:
|
||||
propdataprefix DT_STRING
|
||||
{
|
||||
$$ = data_merge($1, $2);
|
||||
}
|
||||
| propdataprefix arrayprefix '>'
|
||||
{
|
||||
$$ = data_merge($1, $2.data);
|
||||
}
|
||||
| propdataprefix '[' bytestring ']'
|
||||
{
|
||||
$$ = data_merge($1, $3);
|
||||
}
|
||||
| propdataprefix dt_ref
|
||||
{
|
||||
$1 = data_add_marker($1, TYPE_STRING, $2);
|
||||
$$ = data_add_marker($1, REF_PATH, $2);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')'
|
||||
{
|
||||
FILE *f = srcfile_relative_open($4.val, NULL);
|
||||
struct data d;
|
||||
|
||||
if ($6 != 0)
|
||||
if (fseek(f, $6, SEEK_SET) != 0)
|
||||
die("Couldn't seek to offset %llu in \"%s\": %s",
|
||||
(unsigned long long)$6, $4.val,
|
||||
strerror(errno));
|
||||
|
||||
d = data_copy_file(f, $8);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
fclose(f);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ')'
|
||||
{
|
||||
FILE *f = srcfile_relative_open($4.val, NULL);
|
||||
struct data d = empty_data;
|
||||
|
||||
d = data_copy_file(f, -1);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
fclose(f);
|
||||
}
|
||||
| propdata DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
nodedef: '{' proplist subnodes '}' ';' {
|
||||
$$ = build_node($2, $3);
|
||||
}
|
||||
;
|
||||
|
||||
propdataprefix:
|
||||
/* empty */
|
||||
{
|
||||
$$ = empty_data;
|
||||
proplist: propdef proplist {
|
||||
$$ = chain_property($1, $2);
|
||||
}
|
||||
| propdata ','
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| propdataprefix DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
arrayprefix:
|
||||
DT_BITS DT_LITERAL '<'
|
||||
{
|
||||
unsigned long long bits;
|
||||
enum markertype type = TYPE_UINT32;
|
||||
|
||||
bits = $2;
|
||||
|
||||
switch (bits) {
|
||||
case 8: type = TYPE_UINT8; break;
|
||||
case 16: type = TYPE_UINT16; break;
|
||||
case 32: type = TYPE_UINT32; break;
|
||||
case 64: type = TYPE_UINT64; break;
|
||||
default:
|
||||
ERROR(&@2, "Array elements must be"
|
||||
" 8, 16, 32 or 64-bits");
|
||||
bits = 32;
|
||||
}
|
||||
|
||||
$$.data = data_add_marker(empty_data, type, NULL);
|
||||
$$.bits = bits;
|
||||
}
|
||||
| '<'
|
||||
{
|
||||
$$.data = data_add_marker(empty_data, TYPE_UINT32, NULL);
|
||||
$$.bits = 32;
|
||||
}
|
||||
| arrayprefix integer_prim
|
||||
{
|
||||
if ($1.bits < 64) {
|
||||
uint64_t mask = (1ULL << $1.bits) - 1;
|
||||
/*
|
||||
* Bits above mask must either be all zero
|
||||
* (positive within range of mask) or all one
|
||||
* (negative and sign-extended). The second
|
||||
* condition is true if when we set all bits
|
||||
* within the mask to one (i.e. | in the
|
||||
* mask), all bits are one.
|
||||
*/
|
||||
if (($2 > mask) && (($2 | mask) != -1ULL)) {
|
||||
char *loc = srcpos_string(&@2);
|
||||
fprintf(stderr,
|
||||
"WARNING: %s: Value 0x%016" PRIx64
|
||||
" truncated to 0x%0*" PRIx64 "\n",
|
||||
loc, $2, $1.bits / 4, ($2 & mask));
|
||||
free(loc);
|
||||
}
|
||||
}
|
||||
|
||||
$$.data = data_append_integer($1.data, $2, $1.bits);
|
||||
}
|
||||
| arrayprefix dt_ref
|
||||
{
|
||||
uint64_t val = ~0ULL >> (64 - $1.bits);
|
||||
|
||||
if ($1.bits == 32)
|
||||
$1.data = data_add_marker($1.data,
|
||||
REF_PHANDLE,
|
||||
$2);
|
||||
else
|
||||
ERROR(&@2, "References are only allowed in "
|
||||
"arrays with 32-bit elements.");
|
||||
|
||||
$$.data = data_append_integer($1.data, val, $1.bits);
|
||||
}
|
||||
| arrayprefix DT_LABEL
|
||||
{
|
||||
$$.data = data_add_marker($1.data, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
integer_prim:
|
||||
DT_LITERAL
|
||||
| DT_CHAR_LITERAL
|
||||
| '(' integer_expr ')'
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
integer_expr:
|
||||
integer_trinary
|
||||
;
|
||||
|
||||
integer_trinary:
|
||||
integer_or
|
||||
| integer_or '?' integer_expr ':' integer_trinary { $$ = $1 ? $3 : $5; }
|
||||
;
|
||||
|
||||
integer_or:
|
||||
integer_and
|
||||
| integer_or DT_OR integer_and { $$ = $1 || $3; }
|
||||
;
|
||||
|
||||
integer_and:
|
||||
integer_bitor
|
||||
| integer_and DT_AND integer_bitor { $$ = $1 && $3; }
|
||||
;
|
||||
|
||||
integer_bitor:
|
||||
integer_bitxor
|
||||
| integer_bitor '|' integer_bitxor { $$ = $1 | $3; }
|
||||
;
|
||||
|
||||
integer_bitxor:
|
||||
integer_bitand
|
||||
| integer_bitxor '^' integer_bitand { $$ = $1 ^ $3; }
|
||||
;
|
||||
|
||||
integer_bitand:
|
||||
integer_eq
|
||||
| integer_bitand '&' integer_eq { $$ = $1 & $3; }
|
||||
;
|
||||
|
||||
integer_eq:
|
||||
integer_rela
|
||||
| integer_eq DT_EQ integer_rela { $$ = $1 == $3; }
|
||||
| integer_eq DT_NE integer_rela { $$ = $1 != $3; }
|
||||
;
|
||||
|
||||
integer_rela:
|
||||
integer_shift
|
||||
| integer_rela '<' integer_shift { $$ = $1 < $3; }
|
||||
| integer_rela '>' integer_shift { $$ = $1 > $3; }
|
||||
| integer_rela DT_LE integer_shift { $$ = $1 <= $3; }
|
||||
| integer_rela DT_GE integer_shift { $$ = $1 >= $3; }
|
||||
;
|
||||
|
||||
integer_shift:
|
||||
integer_shift DT_LSHIFT integer_add { $$ = ($3 < 64) ? ($1 << $3) : 0; }
|
||||
| integer_shift DT_RSHIFT integer_add { $$ = ($3 < 64) ? ($1 >> $3) : 0; }
|
||||
| integer_add
|
||||
;
|
||||
|
||||
integer_add:
|
||||
integer_add '+' integer_mul { $$ = $1 + $3; }
|
||||
| integer_add '-' integer_mul { $$ = $1 - $3; }
|
||||
| integer_mul
|
||||
;
|
||||
|
||||
integer_mul:
|
||||
integer_mul '*' integer_unary { $$ = $1 * $3; }
|
||||
| integer_mul '/' integer_unary
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 / $3;
|
||||
} else {
|
||||
ERROR(&@$, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| integer_mul '%' integer_unary
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 % $3;
|
||||
} else {
|
||||
ERROR(&@$, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| integer_unary
|
||||
;
|
||||
|
||||
integer_unary:
|
||||
integer_prim
|
||||
| '-' integer_unary { $$ = -$2; }
|
||||
| '~' integer_unary { $$ = ~$2; }
|
||||
| '!' integer_unary { $$ = !$2; }
|
||||
;
|
||||
|
||||
bytestring:
|
||||
/* empty */
|
||||
{
|
||||
$$ = data_add_marker(empty_data, TYPE_UINT8, NULL);
|
||||
}
|
||||
| bytestring DT_BYTE
|
||||
{
|
||||
$$ = data_append_byte($1, $2);
|
||||
}
|
||||
| bytestring DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
subnodes:
|
||||
/* empty */
|
||||
{
|
||||
| /* empty */ {
|
||||
$$ = NULL;
|
||||
}
|
||||
| subnode subnodes
|
||||
{
|
||||
;
|
||||
|
||||
propdef: label DT_PROPNAME '=' propdata ';' {
|
||||
$$ = build_property($2, $4, $1);
|
||||
}
|
||||
| label DT_PROPNAME ';' {
|
||||
$$ = build_property($2, empty_data, $1);
|
||||
}
|
||||
;
|
||||
|
||||
propdata: DT_STRING { $$ = $1; }
|
||||
| '<' celllist '>' { $$ = $2; }
|
||||
| '[' bytestring ']' { $$ = $2; }
|
||||
;
|
||||
|
||||
celllist: celllist DT_CELL { $$ = data_append_cell($1, $2); }
|
||||
| celllist DT_REF {
|
||||
$$ = data_append_cell(data_add_fixup($1, $2), -1);
|
||||
}
|
||||
| /* empty */ { $$ = empty_data; }
|
||||
;
|
||||
|
||||
bytestring: bytestring DT_BYTE { $$ = data_append_byte($1, $2); }
|
||||
| /* empty */ { $$ = empty_data; }
|
||||
;
|
||||
|
||||
subnodes: subnode subnodes {
|
||||
$$ = chain_node($1, $2);
|
||||
}
|
||||
| subnode propdef
|
||||
{
|
||||
ERROR(&@2, "Properties must precede subnodes");
|
||||
YYERROR;
|
||||
}
|
||||
| /* empty */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
subnode:
|
||||
DT_PROPNODENAME nodedef
|
||||
{
|
||||
$$ = name_node($2, $1);
|
||||
free($1);
|
||||
}
|
||||
| DT_DEL_NODE DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = name_node(build_node_delete(&@$), $2);
|
||||
free($2);
|
||||
}
|
||||
| DT_OMIT_NO_REF subnode
|
||||
{
|
||||
$$ = omit_node_if_unused($2);
|
||||
}
|
||||
| DT_LABEL subnode
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
}
|
||||
subnode: label nodename nodedef { $$ = name_node($3, $2, $1); }
|
||||
;
|
||||
|
||||
nodename: DT_NODENAME { $$ = $1; }
|
||||
| DT_PROPNAME { $$ = $1; }
|
||||
;
|
||||
|
||||
label: DT_LABEL { $$ = $1; }
|
||||
| /* empty */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(char const *s)
|
||||
void yyerror (char const *s)
|
||||
{
|
||||
ERROR(&yylloc, "%s", s);
|
||||
fprintf (stderr, "%s at line %d\n", s, yylloc.first_line);
|
||||
}
|
||||
|
|
|
|||
403
dtc.c
403
dtc.c
|
|
@ -1,37 +1,53 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
int quiet; /* Level of quietness */
|
||||
unsigned int reservenum;/* Number of memory reservation slots */
|
||||
int minsize; /* Minimum blob size */
|
||||
int padsize; /* Additional padding to blob */
|
||||
int alignsize; /* Additional padding to blob according to the alignsize */
|
||||
int phandle_format = PHANDLE_EPAPR; /* Use linux,phandle or phandle properties */
|
||||
int generate_symbols; /* enable symbols & fixup support */
|
||||
int generate_fixups; /* suppress generation of fixups on symbol support */
|
||||
int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
int annotate; /* Level of annotation: 1 for input source location
|
||||
>1 for full input source location. */
|
||||
|
||||
static int is_power_of_2(int x)
|
||||
char *join_path(char *path, char *name)
|
||||
{
|
||||
return (x > 0) && ((x & (x - 1)) == 0);
|
||||
int lenp = strlen(path);
|
||||
int lenn = strlen(name);
|
||||
int len;
|
||||
int needslash = 1;
|
||||
char *str;
|
||||
|
||||
len = lenp + lenn + 2;
|
||||
if ((lenp > 0) && (path[lenp-1] == '/')) {
|
||||
needslash = 0;
|
||||
len--;
|
||||
}
|
||||
|
||||
str = xmalloc(len);
|
||||
memcpy(str, path, lenp);
|
||||
if (needslash) {
|
||||
str[lenp] = '/';
|
||||
lenp++;
|
||||
}
|
||||
memcpy(str+lenp, name, lenn+1);
|
||||
return str;
|
||||
}
|
||||
|
||||
static void fill_fullpaths(struct node *tree, const char *prefix)
|
||||
void fill_fullpaths(struct node *tree, char *prefix)
|
||||
{
|
||||
struct node *child;
|
||||
const char *unit;
|
||||
char *unit;
|
||||
|
||||
tree->fullpath = join_path(prefix, tree->name);
|
||||
|
||||
|
|
@ -45,143 +61,60 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
|
|||
fill_fullpaths(child, tree->fullpath);
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] = "dtc [options] <input file>";
|
||||
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@LAThv";
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"quiet", no_argument, NULL, 'q'},
|
||||
{"in-format", a_argument, NULL, 'I'},
|
||||
{"out", a_argument, NULL, 'o'},
|
||||
{"out-format", a_argument, NULL, 'O'},
|
||||
{"out-version", a_argument, NULL, 'V'},
|
||||
{"out-dependency", a_argument, NULL, 'd'},
|
||||
{"reserve", a_argument, NULL, 'R'},
|
||||
{"space", a_argument, NULL, 'S'},
|
||||
{"pad", a_argument, NULL, 'p'},
|
||||
{"align", a_argument, NULL, 'a'},
|
||||
{"boot-cpu", a_argument, NULL, 'b'},
|
||||
{"force", no_argument, NULL, 'f'},
|
||||
{"include", a_argument, NULL, 'i'},
|
||||
{"sort", no_argument, NULL, 's'},
|
||||
{"phandle", a_argument, NULL, 'H'},
|
||||
{"warning", a_argument, NULL, 'W'},
|
||||
{"error", a_argument, NULL, 'E'},
|
||||
{"symbols", no_argument, NULL, '@'},
|
||||
{"local-fixups", no_argument, NULL, 'L'},
|
||||
{"auto-alias", no_argument, NULL, 'A'},
|
||||
{"annotate", no_argument, NULL, 'T'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, no_argument, NULL, 0x0},
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"\n\tQuiet: -q suppress warnings, -qq errors, -qqq all",
|
||||
"\n\tInput formats are:\n"
|
||||
"\t\tdts - device tree source text\n"
|
||||
"\t\tdtb - device tree blob\n"
|
||||
"\t\tfs - /proc/device-tree style directory",
|
||||
"\n\tOutput file",
|
||||
"\n\tOutput formats are:\n"
|
||||
"\t\tdts - device tree source text\n"
|
||||
"\t\tdtb - device tree blob\n"
|
||||
#ifndef NO_YAML
|
||||
"\t\tyaml - device tree encoded as YAML\n"
|
||||
#endif
|
||||
"\t\tasm - assembler source",
|
||||
"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
|
||||
"\n\tOutput dependency file",
|
||||
"\n\tMake space for <number> reserve map entries (for dtb and asm output)",
|
||||
"\n\tMake the blob at least <bytes> long (extra space)",
|
||||
"\n\tAdd padding to the blob of <bytes> long (extra space)",
|
||||
"\n\tMake the blob align to the <bytes> (extra space)",
|
||||
"\n\tSet the physical boot cpu",
|
||||
"\n\tTry to produce output even if the input tree has errors",
|
||||
"\n\tAdd a path to search for include files",
|
||||
"\n\tSort nodes and properties before outputting (useful for comparing trees)",
|
||||
"\n\tValid phandle formats are:\n"
|
||||
"\t\tlegacy - \"linux,phandle\" properties only\n"
|
||||
"\t\tepapr - \"phandle\" properties only\n"
|
||||
"\t\tboth - Both \"linux,phandle\" and \"phandle\" properties",
|
||||
"\n\tEnable/disable warnings (prefix with \"no-\")",
|
||||
"\n\tEnable/disable errors (prefix with \"no-\")",
|
||||
"\n\tEnable generation of symbols",
|
||||
"\n\tPossibly generates a __local_fixups__ and a __fixups__ node at the root node",
|
||||
"\n\tEnable auto-alias of labels",
|
||||
"\n\tAnnotate output .dts with input source file and line (-T -T for more details)",
|
||||
"\n\tPrint this help and exit",
|
||||
"\n\tPrint version and exit",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *guess_type_by_name(const char *fname, const char *fallback)
|
||||
static FILE *dtc_open_file(char *fname)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
s = strrchr(fname, '.');
|
||||
if (s == NULL)
|
||||
return fallback;
|
||||
if (!strcasecmp(s, ".dts"))
|
||||
return "dts";
|
||||
if (!strcasecmp(s, ".yaml"))
|
||||
return "yaml";
|
||||
if (!strcasecmp(s, ".dtbo"))
|
||||
return "dtb";
|
||||
if (!strcasecmp(s, ".dtb"))
|
||||
return "dtb";
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static const char *guess_input_format(const char *fname, const char *fallback)
|
||||
{
|
||||
struct stat statbuf;
|
||||
fdt32_t magic;
|
||||
FILE *f;
|
||||
|
||||
if (stat(fname, &statbuf) != 0)
|
||||
return fallback;
|
||||
if (streq(fname, "-"))
|
||||
f = stdin;
|
||||
else
|
||||
f = fopen(fname, "r");
|
||||
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
return "fs";
|
||||
if (! f)
|
||||
die("Couldn't open \"%s\": %s\n", fname, strerror(errno));
|
||||
|
||||
if (!S_ISREG(statbuf.st_mode))
|
||||
return fallback;
|
||||
return f;
|
||||
}
|
||||
|
||||
f = fopen(fname, "r");
|
||||
if (f == NULL)
|
||||
return fallback;
|
||||
if (fread(&magic, 4, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return fallback;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (fdt32_to_cpu(magic) == FDT_MAGIC)
|
||||
return "dtb";
|
||||
|
||||
return guess_type_by_name(fname, fallback);
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tdtc [options] <input file>\n");
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
fprintf(stderr, "\t-I <input format>\n");
|
||||
fprintf(stderr, "\t\tInput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tfs - /proc/device-tree style directory\n");
|
||||
fprintf(stderr, "\t-O <output format>\n");
|
||||
fprintf(stderr, "\t\tOutput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tasm - assembler source\n");
|
||||
fprintf(stderr, "\t-V <output version>\n");
|
||||
fprintf(stderr, "\t\tBlob version to produce, defaults to 3 (relevant for dtb\n\t\tand asm output only)\n");
|
||||
fprintf(stderr, "\t-R <number>\n");
|
||||
fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n");
|
||||
fprintf(stderr, "\t-f\n");
|
||||
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct dt_info *dti;
|
||||
const char *inform = NULL;
|
||||
const char *outform = NULL;
|
||||
const char *outname = "-";
|
||||
const char *depname = NULL;
|
||||
bool force = false, sort = false;
|
||||
const char *arg;
|
||||
struct boot_info *bi;
|
||||
char *inform = "dts";
|
||||
char *outform = "dts";
|
||||
char *outname = "-";
|
||||
int force = 0;
|
||||
char *arg;
|
||||
int opt;
|
||||
FILE *inf = NULL;
|
||||
FILE *outf = NULL;
|
||||
int outversion = DEFAULT_FDT_VERSION;
|
||||
long long cmdline_boot_cpuid = -1;
|
||||
int outversion = 3;
|
||||
int reservenum = 1;
|
||||
|
||||
quiet = 0;
|
||||
reservenum = 0;
|
||||
minsize = 0;
|
||||
padsize = 0;
|
||||
alignsize = 0;
|
||||
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
while ((opt = getopt(argc, argv, "I:O:o:V:R:f")) != EOF) {
|
||||
switch (opt) {
|
||||
case 'I':
|
||||
inform = optarg;
|
||||
|
|
@ -195,191 +128,71 @@ int main(int argc, char *argv[])
|
|||
case 'V':
|
||||
outversion = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'd':
|
||||
depname = optarg;
|
||||
break;
|
||||
case 'R':
|
||||
reservenum = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'S':
|
||||
minsize = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'p':
|
||||
padsize = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'a':
|
||||
alignsize = strtol(optarg, NULL, 0);
|
||||
if (!is_power_of_2(alignsize))
|
||||
die("Invalid argument \"%d\" to -a option\n",
|
||||
alignsize);
|
||||
reservenum = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
force = true;
|
||||
force = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet++;
|
||||
break;
|
||||
case 'b':
|
||||
cmdline_boot_cpuid = strtoll(optarg, NULL, 0);
|
||||
break;
|
||||
case 'i':
|
||||
srcfile_add_search_path(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
util_version();
|
||||
case 'H':
|
||||
if (streq(optarg, "legacy"))
|
||||
phandle_format = PHANDLE_LEGACY;
|
||||
else if (streq(optarg, "epapr"))
|
||||
phandle_format = PHANDLE_EPAPR;
|
||||
else if (streq(optarg, "both"))
|
||||
phandle_format = PHANDLE_BOTH;
|
||||
else
|
||||
die("Invalid argument \"%s\" to -H option\n",
|
||||
optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
sort = true;
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
parse_checks_option(true, false, optarg);
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
parse_checks_option(false, true, optarg);
|
||||
break;
|
||||
|
||||
case '@':
|
||||
generate_symbols = 1;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
generate_fixups = 1;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
auto_label_aliases = 1;
|
||||
break;
|
||||
case 'T':
|
||||
annotate++;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage(NULL);
|
||||
default:
|
||||
usage("unknown option");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > (optind+1))
|
||||
usage("missing files");
|
||||
usage();
|
||||
else if (argc < (optind+1))
|
||||
arg = "-";
|
||||
else
|
||||
arg = argv[optind];
|
||||
|
||||
/* minsize and padsize are mutually exclusive */
|
||||
if (minsize && padsize)
|
||||
die("Can't set both -p and -S\n");
|
||||
fprintf(stderr, "DTC: %s->%s on file \"%s\"\n",
|
||||
inform, outform, arg);
|
||||
|
||||
if (depname) {
|
||||
depfile = fopen(depname, "w");
|
||||
if (!depfile)
|
||||
die("Couldn't open dependency file %s: %s\n", depname,
|
||||
strerror(errno));
|
||||
|
||||
fprint_path_escaped(depfile, outname);
|
||||
fputc(':', depfile);
|
||||
}
|
||||
|
||||
if (inform == NULL)
|
||||
inform = guess_input_format(arg, "dts");
|
||||
if (outform == NULL) {
|
||||
outform = guess_type_by_name(outname, NULL);
|
||||
if (outform == NULL) {
|
||||
if (streq(inform, "dts"))
|
||||
outform = "dtb";
|
||||
else
|
||||
outform = "dts";
|
||||
}
|
||||
}
|
||||
if (annotate && (!streq(inform, "dts") || !streq(outform, "dts")))
|
||||
die("--annotate requires -I dts -O dts\n");
|
||||
if (streq(inform, "dts"))
|
||||
dti = dt_from_source(arg);
|
||||
else if (streq(inform, "fs"))
|
||||
dti = dt_from_fs(arg);
|
||||
else if(streq(inform, "dtb"))
|
||||
dti = dt_from_blob(arg);
|
||||
else
|
||||
if (streq(inform, "dts")) {
|
||||
inf = dtc_open_file(arg);
|
||||
bi = dt_from_source(inf);
|
||||
} else if (streq(inform, "fs")) {
|
||||
bi = dt_from_fs(arg);
|
||||
} else if(streq(inform, "dtb")) {
|
||||
inf = dtc_open_file(arg);
|
||||
bi = dt_from_blob(inf);
|
||||
} else {
|
||||
die("Unknown input format \"%s\"\n", inform);
|
||||
|
||||
dti->outname = outname;
|
||||
|
||||
if (depfile) {
|
||||
fputc('\n', depfile);
|
||||
fclose(depfile);
|
||||
}
|
||||
|
||||
if (cmdline_boot_cpuid != -1)
|
||||
dti->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
if (inf && (inf != stdin))
|
||||
fclose(inf);
|
||||
|
||||
fill_fullpaths(dti->dt, "");
|
||||
if (! bi || ! bi->dt)
|
||||
die("Couldn't read input tree\n");
|
||||
|
||||
/* on a plugin, generate by default */
|
||||
if (dti->dtsflags & DTSF_PLUGIN) {
|
||||
generate_fixups = 1;
|
||||
if (! check_device_tree(bi->dt)) {
|
||||
fprintf(stderr, "Input tree has errors\n");
|
||||
if (! force)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
process_checks(force, dti);
|
||||
|
||||
if (auto_label_aliases)
|
||||
generate_label_tree(dti, "aliases", false);
|
||||
|
||||
generate_labels_from_tree(dti, "__symbols__");
|
||||
|
||||
if (generate_symbols)
|
||||
generate_label_tree(dti, "__symbols__", true);
|
||||
|
||||
fixup_phandles(dti, "__fixups__");
|
||||
local_fixup_phandles(dti, "__local_fixups__");
|
||||
|
||||
if (generate_fixups) {
|
||||
generate_fixups_tree(dti, "__fixups__");
|
||||
generate_local_fixups_tree(dti, "__local_fixups__");
|
||||
}
|
||||
|
||||
if (sort)
|
||||
sort_tree(dti);
|
||||
|
||||
if (streq(outname, "-")) {
|
||||
outf = stdout;
|
||||
} else {
|
||||
outf = fopen(outname, "wb");
|
||||
outf = fopen(outname, "w");
|
||||
if (! outf)
|
||||
die("Couldn't open output file %s: %s\n",
|
||||
outname, strerror(errno));
|
||||
}
|
||||
|
||||
if (streq(outform, "dts")) {
|
||||
dt_to_source(outf, dti);
|
||||
#ifndef NO_YAML
|
||||
} else if (streq(outform, "yaml")) {
|
||||
if (!streq(inform, "dts"))
|
||||
die("YAML output format requires dts input format\n");
|
||||
dt_to_yaml(outf, dti);
|
||||
#endif
|
||||
dt_to_source(outf, bi);
|
||||
} else if (streq(outform, "dtb")) {
|
||||
dt_to_blob(outf, dti, outversion);
|
||||
dt_to_blob(outf, bi, outversion);
|
||||
} else if (streq(outform, "asm")) {
|
||||
dt_to_asm(outf, dti, outversion);
|
||||
dt_to_asm(outf, bi, outversion);
|
||||
} else if (streq(outform, "null")) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
die("Unknown output format \"%s\"\n", outform);
|
||||
}
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
374
dtc.h
374
dtc.h
|
|
@ -1,194 +1,134 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef DTC_H
|
||||
#define DTC_H
|
||||
#ifndef _DTC_H
|
||||
#define _DTC_H
|
||||
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <netinet/in.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#include <libfdt_env.h>
|
||||
#include <fdt.h>
|
||||
#include "flat_dt.h"
|
||||
|
||||
#include "util.h"
|
||||
static inline void die(char * str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
va_start(ap, str);
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
vfprintf(stderr, str, ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void *xmalloc(size_t len)
|
||||
{
|
||||
void *new = malloc(len);
|
||||
|
||||
if (! new)
|
||||
die("malloc() failed\n");
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static inline void *xrealloc(void *p, size_t len)
|
||||
{
|
||||
void *new = realloc(p, len);
|
||||
|
||||
if (! new)
|
||||
die("realloc() failed (len=%d)\n", len);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef u32 cell_t;
|
||||
|
||||
#define cpu_to_be16(x) htons(x)
|
||||
#define be16_to_cpu(x) ntohs(x)
|
||||
|
||||
#define cpu_to_be32(x) htonl(x)
|
||||
#define be32_to_cpu(x) ntohl(x)
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define cpu_to_be64(x) (x)
|
||||
#define be64_to_cpu(x) (x)
|
||||
#else
|
||||
#define debug(...)
|
||||
#define cpu_to_be64(x) bswap_64(x)
|
||||
#define be64_to_cpu(x) bswap_64(x)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FDT_VERSION 17
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
extern int quiet; /* Level of quietness */
|
||||
extern unsigned int reservenum; /* Number of memory reservation slots */
|
||||
extern int minsize; /* Minimum blob size */
|
||||
extern int padsize; /* Additional padding to blob */
|
||||
extern int alignsize; /* Additional padding to blob according to the alignsize */
|
||||
extern int phandle_format; /* Use linux,phandle or phandle properties */
|
||||
extern int generate_symbols; /* generate symbols for nodes with labels */
|
||||
extern int generate_fixups; /* generate fixups */
|
||||
extern int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
extern int annotate; /* annotate .dts with input source location */
|
||||
|
||||
#define PHANDLE_LEGACY 0x1
|
||||
#define PHANDLE_EPAPR 0x2
|
||||
#define PHANDLE_BOTH 0x3
|
||||
|
||||
typedef uint32_t cell_t;
|
||||
|
||||
static inline bool phandle_is_valid(cell_t phandle)
|
||||
{
|
||||
return phandle != 0 && phandle != ~0U;
|
||||
}
|
||||
|
||||
static inline uint16_t dtb_ld16(const void *p)
|
||||
{
|
||||
const uint8_t *bp = (const uint8_t *)p;
|
||||
|
||||
return ((uint16_t)bp[0] << 8)
|
||||
| bp[1];
|
||||
}
|
||||
|
||||
static inline uint32_t dtb_ld32(const void *p)
|
||||
{
|
||||
const uint8_t *bp = (const uint8_t *)p;
|
||||
|
||||
return ((uint32_t)bp[0] << 24)
|
||||
| ((uint32_t)bp[1] << 16)
|
||||
| ((uint32_t)bp[2] << 8)
|
||||
| bp[3];
|
||||
}
|
||||
|
||||
static inline uint64_t dtb_ld64(const void *p)
|
||||
{
|
||||
const uint8_t *bp = (const uint8_t *)p;
|
||||
|
||||
return ((uint64_t)bp[0] << 56)
|
||||
| ((uint64_t)bp[1] << 48)
|
||||
| ((uint64_t)bp[2] << 40)
|
||||
| ((uint64_t)bp[3] << 32)
|
||||
| ((uint64_t)bp[4] << 24)
|
||||
| ((uint64_t)bp[5] << 16)
|
||||
| ((uint64_t)bp[6] << 8)
|
||||
| bp[7];
|
||||
}
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
#define strstarts(s, prefix) (strncmp((s), (prefix), strlen(prefix)) == 0)
|
||||
#define strprefixeq(a, n, b) (strlen(b) == (n) && (memcmp(a, b, n) == 0))
|
||||
static inline bool strends(const char *str, const char *suffix)
|
||||
{
|
||||
unsigned int len, suffix_len;
|
||||
|
||||
len = strlen(str);
|
||||
suffix_len = strlen(suffix);
|
||||
if (len < suffix_len)
|
||||
return false;
|
||||
return streq(str + len - suffix_len, suffix);
|
||||
}
|
||||
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
|
||||
|
||||
#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/* Data blobs */
|
||||
enum markertype {
|
||||
TYPE_NONE,
|
||||
REF_PHANDLE,
|
||||
REF_PATH,
|
||||
LABEL,
|
||||
TYPE_UINT8,
|
||||
TYPE_UINT16,
|
||||
TYPE_UINT32,
|
||||
TYPE_UINT64,
|
||||
TYPE_STRING,
|
||||
};
|
||||
|
||||
static inline bool is_type_marker(enum markertype type)
|
||||
{
|
||||
return type >= TYPE_UINT8;
|
||||
}
|
||||
|
||||
extern const char *markername(enum markertype markertype);
|
||||
|
||||
struct marker {
|
||||
enum markertype type;
|
||||
unsigned int offset;
|
||||
struct fixup {
|
||||
int offset;
|
||||
char *ref;
|
||||
struct marker *next;
|
||||
struct fixup *next;
|
||||
};
|
||||
|
||||
struct data {
|
||||
unsigned int len;
|
||||
int len;
|
||||
char *val;
|
||||
struct marker *markers;
|
||||
int asize;
|
||||
struct fixup *refs;
|
||||
};
|
||||
|
||||
#define empty_data \
|
||||
((struct data){.len = 0, .val = NULL, .asize = 0, .refs = NULL})
|
||||
|
||||
#define empty_data ((struct data){ 0 /* all .members = 0 or NULL */ })
|
||||
|
||||
#define for_each_marker(m) \
|
||||
for (; (m); (m) = (m)->next)
|
||||
#define for_each_marker_of_type(m, t) \
|
||||
for_each_marker(m) \
|
||||
if ((m)->type == (t))
|
||||
|
||||
static inline struct marker *next_type_marker(struct marker *m)
|
||||
{
|
||||
for_each_marker(m)
|
||||
if (is_type_marker(m->type))
|
||||
break;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline size_t type_marker_length(struct marker *m)
|
||||
{
|
||||
struct marker *next = next_type_marker(m->next);
|
||||
|
||||
if (next)
|
||||
return next->offset - m->offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fixup_free(struct fixup *f);
|
||||
void data_free(struct data d);
|
||||
|
||||
struct data data_grow_for(struct data d, unsigned int xlen);
|
||||
struct data data_grow_for(struct data d, int xlen);
|
||||
|
||||
struct data data_copy_mem(const char *mem, int len);
|
||||
struct data data_copy_escape_string(const char *s, int len);
|
||||
struct data data_copy_mem(char *mem, int len);
|
||||
struct data data_copy_escape_string(char *s, int len);
|
||||
struct data data_copy_file(FILE *f, size_t len);
|
||||
|
||||
struct data data_append_data(struct data d, const void *p, int len);
|
||||
struct data data_insert_at_marker(struct data d, struct marker *m,
|
||||
const void *p, int len);
|
||||
struct data data_merge(struct data d1, struct data d2);
|
||||
struct data data_append_data(struct data d, void *p, int len);
|
||||
struct data data_append_cell(struct data d, cell_t word);
|
||||
struct data data_append_integer(struct data d, uint64_t word, int bits);
|
||||
struct data data_append_re(struct data d, uint64_t address, uint64_t size);
|
||||
struct data data_append_addr(struct data d, uint64_t addr);
|
||||
struct data data_append_re(struct data d, struct reserve_entry *re);
|
||||
struct data data_append_addr(struct data d, u64 addr);
|
||||
struct data data_append_byte(struct data d, uint8_t byte);
|
||||
struct data data_append_zeroes(struct data d, int len);
|
||||
struct data data_append_align(struct data d, int align);
|
||||
struct data data_insert_data(struct data d, struct marker *m, struct data old);
|
||||
|
||||
struct marker *alloc_marker(unsigned int offset, enum markertype type,
|
||||
char *ref);
|
||||
struct data data_add_marker(struct data d, enum markertype type, char *ref);
|
||||
struct data data_add_fixup(struct data d, char *ref);
|
||||
|
||||
bool data_is_one_string(struct data d);
|
||||
int data_is_one_string(struct data d);
|
||||
|
||||
/* DT constraints */
|
||||
|
||||
|
|
@ -196,29 +136,16 @@ bool data_is_one_string(struct data d);
|
|||
#define MAX_NODENAME_LEN 31
|
||||
|
||||
/* Live trees */
|
||||
struct label {
|
||||
bool deleted;
|
||||
char *label;
|
||||
struct label *next;
|
||||
};
|
||||
|
||||
struct bus_type {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct property {
|
||||
bool deleted;
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
struct property *next;
|
||||
|
||||
struct label *labels;
|
||||
struct srcpos *srcpos;
|
||||
char *label;
|
||||
};
|
||||
|
||||
struct node {
|
||||
bool deleted;
|
||||
char *name;
|
||||
struct property *proplist;
|
||||
struct node *children;
|
||||
|
|
@ -232,146 +159,71 @@ struct node {
|
|||
cell_t phandle;
|
||||
int addr_cells, size_cells;
|
||||
|
||||
struct label *labels;
|
||||
const struct bus_type *bus;
|
||||
struct srcpos *srcpos;
|
||||
|
||||
bool omit_if_unused, is_referenced;
|
||||
char *label;
|
||||
};
|
||||
|
||||
#define for_each_label_withdel(l0, l) \
|
||||
for ((l) = (l0); (l); (l) = (l)->next)
|
||||
|
||||
#define for_each_label(l0, l) \
|
||||
for_each_label_withdel(l0, l) \
|
||||
if (!(l)->deleted)
|
||||
|
||||
#define for_each_property_withdel(n, p) \
|
||||
#define for_each_property(n, p) \
|
||||
for ((p) = (n)->proplist; (p); (p) = (p)->next)
|
||||
|
||||
#define for_each_property(n, p) \
|
||||
for_each_property_withdel(n, p) \
|
||||
if (!(p)->deleted)
|
||||
|
||||
#define for_each_child_withdel(n, c) \
|
||||
#define for_each_child(n, c) \
|
||||
for ((c) = (n)->children; (c); (c) = (c)->next_sibling)
|
||||
|
||||
#define for_each_child(n, c) \
|
||||
for_each_child_withdel(n, c) \
|
||||
if (!(c)->deleted)
|
||||
|
||||
void add_label(struct label **labels, char *label);
|
||||
void delete_labels(struct label **labels);
|
||||
|
||||
struct property *build_property(const char *name, struct data val,
|
||||
struct srcpos *srcpos);
|
||||
struct property *build_property_delete(const char *name);
|
||||
struct property *build_property(char *name, struct data val, char *label);
|
||||
struct property *chain_property(struct property *first, struct property *list);
|
||||
struct property *reverse_properties(struct property *first);
|
||||
|
||||
struct node *build_node(struct property *proplist, struct node *children,
|
||||
struct srcpos *srcpos);
|
||||
struct node *build_node_delete(struct srcpos *srcpos);
|
||||
struct node *name_node(struct node *node, const char *name);
|
||||
struct node *omit_node_if_unused(struct node *node);
|
||||
struct node *reference_node(struct node *node);
|
||||
struct node *build_node(struct property *proplist, struct node *children);
|
||||
struct node *name_node(struct node *node, char *name, char *label);
|
||||
struct node *chain_node(struct node *first, struct node *list);
|
||||
struct node *merge_nodes(struct node *old_node, struct node *new_node);
|
||||
struct node *add_orphan_node(struct node *old_node, struct node *new_node, char *ref);
|
||||
|
||||
void add_property(struct node *node, struct property *prop);
|
||||
void delete_property_by_name(struct node *node, char *name);
|
||||
void delete_property(struct property *prop);
|
||||
void add_child(struct node *parent, struct node *child);
|
||||
void delete_node_by_name(struct node *parent, char *name);
|
||||
void delete_node(struct node *node);
|
||||
void append_to_property(struct node *node,
|
||||
char *name, const void *data, int len,
|
||||
enum markertype type);
|
||||
|
||||
const char *get_unitname(struct node *node);
|
||||
struct property *get_property(struct node *node, const char *propname);
|
||||
cell_t propval_cell(struct property *prop);
|
||||
cell_t propval_cell_n(struct property *prop, unsigned int n);
|
||||
struct property *get_property_by_label(struct node *tree, const char *label,
|
||||
struct node **node);
|
||||
struct marker *get_marker_label(struct node *tree, const char *label,
|
||||
struct node **node, struct property **prop);
|
||||
struct node *get_subnode(struct node *node, const char *nodename);
|
||||
struct node *get_node_by_path(struct node *tree, const char *path);
|
||||
struct node *get_node_by_label(struct node *tree, const char *label);
|
||||
struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
|
||||
struct node *get_node_by_ref(struct node *tree, const char *ref);
|
||||
cell_t get_node_phandle(struct node *root, struct node *node);
|
||||
|
||||
uint32_t guess_boot_cpuid(struct node *tree);
|
||||
int check_device_tree(struct node *dt);
|
||||
|
||||
/* Boot info (tree plus memreserve information */
|
||||
|
||||
struct reserve_info {
|
||||
uint64_t address, size;
|
||||
struct reserve_entry re;
|
||||
|
||||
struct reserve_info *next;
|
||||
|
||||
struct label *labels;
|
||||
char *label;
|
||||
};
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t start, uint64_t len);
|
||||
struct reserve_info *build_reserve_entry(u64 start, u64 len, char *label);
|
||||
struct reserve_info *chain_reserve_entry(struct reserve_info *first,
|
||||
struct reserve_info *list);
|
||||
struct reserve_info *add_reserve_entry(struct reserve_info *list,
|
||||
struct reserve_info *new);
|
||||
|
||||
|
||||
struct dt_info {
|
||||
unsigned int dtsflags;
|
||||
struct boot_info {
|
||||
struct reserve_info *reservelist;
|
||||
uint32_t boot_cpuid_phys;
|
||||
struct node *dt; /* the device tree */
|
||||
const char *outname; /* filename being written to, "-" for stdout */
|
||||
};
|
||||
|
||||
/* DTS version flags definitions */
|
||||
#define DTSF_V1 0x0001 /* /dts-v1/ */
|
||||
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
|
||||
|
||||
struct dt_info *build_dt_info(unsigned int dtsflags,
|
||||
struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys);
|
||||
void sort_tree(struct dt_info *dti);
|
||||
void generate_labels_from_tree(struct dt_info *dti, const char *name);
|
||||
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph);
|
||||
void generate_fixups_tree(struct dt_info *dti, const char *name);
|
||||
void fixup_phandles(struct dt_info *dti, const char *name);
|
||||
void generate_local_fixups_tree(struct dt_info *dti, const char *name);
|
||||
void local_fixup_phandles(struct dt_info *dti, const char *name);
|
||||
|
||||
/* Checks */
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *arg);
|
||||
void process_checks(bool force, struct dt_info *dti);
|
||||
struct boot_info *build_boot_info(struct reserve_info *reservelist,
|
||||
struct node *tree);
|
||||
|
||||
/* Flattened trees */
|
||||
|
||||
void dt_to_blob(FILE *f, struct dt_info *dti, int version);
|
||||
void dt_to_asm(FILE *f, struct dt_info *dti, int version);
|
||||
void dt_to_blob(FILE *f, struct boot_info *bi, int version);
|
||||
void dt_to_asm(FILE *f, struct boot_info *bi, int version);
|
||||
|
||||
struct dt_info *dt_from_blob(const char *fname);
|
||||
struct boot_info *dt_from_blob(FILE *f);
|
||||
|
||||
/* Tree source */
|
||||
|
||||
void property_add_marker(struct property *prop,
|
||||
enum markertype type, unsigned int offset, char *ref);
|
||||
void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int offset);
|
||||
void dt_to_source(FILE *f, struct dt_info *dti);
|
||||
struct dt_info *dt_from_source(const char *f);
|
||||
|
||||
/* YAML source */
|
||||
|
||||
void dt_to_yaml(FILE *f, struct dt_info *dti);
|
||||
void dt_to_source(FILE *f, struct boot_info *bi);
|
||||
struct boot_info *dt_from_source(FILE *f);
|
||||
|
||||
/* FS trees */
|
||||
|
||||
struct dt_info *dt_from_fs(const char *dirname);
|
||||
struct boot_info *dt_from_fs(char *dirname);
|
||||
|
||||
#endif /* DTC_H */
|
||||
/* misc */
|
||||
|
||||
char *join_path(char *path, char *name);
|
||||
void fill_fullpaths(struct node *tree, char *prefix);
|
||||
|
||||
#endif /* _DTC_H */
|
||||
|
|
|
|||
39
dtdiff
39
dtdiff
|
|
@ -1,39 +0,0 @@
|
|||
#! /bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# This script uses the bash <(...) extension.
|
||||
# If you want to change this to work with a generic /bin/sh, make sure
|
||||
# you fix that.
|
||||
|
||||
|
||||
DTC=dtc
|
||||
|
||||
source_and_sort () {
|
||||
DT="$1"
|
||||
if [ -d "$DT" ]; then
|
||||
IFORMAT=fs
|
||||
elif [ -f "$DT" ]; then
|
||||
case "$DT" in
|
||||
*.dts)
|
||||
IFORMAT=dts
|
||||
;;
|
||||
*.dtb|*.dtbo)
|
||||
IFORMAT=dtb
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -z "$IFORMAT" ]; then
|
||||
echo "Unrecognized format for $DT" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
$DTC -I $IFORMAT -O dts -qq -f -s -o - "$DT"
|
||||
}
|
||||
|
||||
if [ $# != 2 ]; then
|
||||
echo "Usage: dtdiff <device tree> <device tree>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
diff -u <(source_and_sort "$1") <(source_and_sort "$2")
|
||||
250
fdtdump.c
250
fdtdump.c
|
|
@ -1,250 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.h>
|
||||
#include <fdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define FDT_MAGIC_SIZE 4
|
||||
#define MAX_VERSION 17U
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
#define PALIGN(p, a) ((void *)(ALIGN((uintptr_t)(p), (a))))
|
||||
#define GET_CELL(p) (p += 4, *((const fdt32_t *)(p-4)))
|
||||
|
||||
static const char *tagname(uint32_t tag)
|
||||
{
|
||||
static const char * const names[] = {
|
||||
#define TN(t) [t] = #t
|
||||
TN(FDT_BEGIN_NODE),
|
||||
TN(FDT_END_NODE),
|
||||
TN(FDT_PROP),
|
||||
TN(FDT_NOP),
|
||||
TN(FDT_END),
|
||||
#undef TN
|
||||
};
|
||||
if (tag < ARRAY_SIZE(names))
|
||||
if (names[tag])
|
||||
return names[tag];
|
||||
return "FDT_???";
|
||||
}
|
||||
|
||||
#define dumpf(fmt, args...) \
|
||||
do { if (debug) printf("// " fmt, ## args); } while (0)
|
||||
|
||||
static void dump_blob(void *blob, bool debug)
|
||||
{
|
||||
uintptr_t blob_off = (uintptr_t)blob;
|
||||
struct fdt_header *bph = blob;
|
||||
uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap);
|
||||
uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct);
|
||||
uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings);
|
||||
struct fdt_reserve_entry *p_rsvmap =
|
||||
(struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
|
||||
const char *p_struct = (const char *)blob + off_dt;
|
||||
const char *p_strings = (const char *)blob + off_str;
|
||||
uint32_t version = fdt32_to_cpu(bph->version);
|
||||
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
|
||||
uint32_t tag;
|
||||
const char *p, *s, *t;
|
||||
int depth, sz, shift;
|
||||
int i;
|
||||
uint64_t addr, size;
|
||||
|
||||
depth = 0;
|
||||
shift = 4;
|
||||
|
||||
printf("/dts-v1/;\n");
|
||||
printf("// magic:\t\t0x%"PRIx32"\n", fdt32_to_cpu(bph->magic));
|
||||
printf("// totalsize:\t\t0x%"PRIx32" (%"PRIu32")\n",
|
||||
totalsize, totalsize);
|
||||
printf("// off_dt_struct:\t0x%"PRIx32"\n", off_dt);
|
||||
printf("// off_dt_strings:\t0x%"PRIx32"\n", off_str);
|
||||
printf("// off_mem_rsvmap:\t0x%"PRIx32"\n", off_mem_rsvmap);
|
||||
printf("// version:\t\t%"PRIu32"\n", version);
|
||||
printf("// last_comp_version:\t%"PRIu32"\n",
|
||||
fdt32_to_cpu(bph->last_comp_version));
|
||||
if (version >= 2)
|
||||
printf("// boot_cpuid_phys:\t0x%"PRIx32"\n",
|
||||
fdt32_to_cpu(bph->boot_cpuid_phys));
|
||||
|
||||
if (version >= 3)
|
||||
printf("// size_dt_strings:\t0x%"PRIx32"\n",
|
||||
fdt32_to_cpu(bph->size_dt_strings));
|
||||
if (version >= 17)
|
||||
printf("// size_dt_struct:\t0x%"PRIx32"\n",
|
||||
fdt32_to_cpu(bph->size_dt_struct));
|
||||
printf("\n");
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
addr = fdt64_to_cpu(p_rsvmap[i].address);
|
||||
size = fdt64_to_cpu(p_rsvmap[i].size);
|
||||
if (addr == 0 && size == 0)
|
||||
break;
|
||||
|
||||
printf("/memreserve/ %#"PRIx64" %#"PRIx64";\n",
|
||||
addr, size);
|
||||
}
|
||||
|
||||
p = p_struct;
|
||||
while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
|
||||
|
||||
dumpf("%04"PRIxPTR": tag: 0x%08"PRIx32" (%s)\n",
|
||||
(uintptr_t)p - blob_off - 4, tag, tagname(tag));
|
||||
|
||||
if (tag == FDT_BEGIN_NODE) {
|
||||
s = p;
|
||||
p = PALIGN(p + strlen(s) + 1, 4);
|
||||
|
||||
if (*s == '\0')
|
||||
s = "/";
|
||||
|
||||
printf("%*s%s {\n", depth * shift, "", s);
|
||||
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == FDT_END_NODE) {
|
||||
depth--;
|
||||
|
||||
printf("%*s};\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == FDT_NOP) {
|
||||
printf("%*s// [NOP]\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == FDT_PROP) {
|
||||
sz = fdt32_to_cpu(GET_CELL(p));
|
||||
s = p_strings + fdt32_to_cpu(GET_CELL(p));
|
||||
if (version < 16 && sz >= 8)
|
||||
p = PALIGN(p, 8);
|
||||
t = p;
|
||||
|
||||
p = PALIGN(p + sz, 4);
|
||||
|
||||
dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
|
||||
dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
|
||||
printf("%*s%s", depth * shift, "", s);
|
||||
utilfdt_print_data(t, sz);
|
||||
printf(";\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] = "fdtdump [options] <file>";
|
||||
static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"debug", no_argument, NULL, 'd'},
|
||||
{"scan", no_argument, NULL, 's'},
|
||||
USAGE_COMMON_LONG_OPTS
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"Dump debug information while decoding the file",
|
||||
"Scan for an embedded fdt in file",
|
||||
USAGE_COMMON_OPTS_HELP
|
||||
};
|
||||
|
||||
static bool valid_header(char *p, size_t len)
|
||||
{
|
||||
if (len < sizeof(struct fdt_header) ||
|
||||
fdt_magic(p) != FDT_MAGIC ||
|
||||
fdt_version(p) > MAX_VERSION ||
|
||||
fdt_last_comp_version(p) > MAX_VERSION ||
|
||||
fdt_totalsize(p) >= len ||
|
||||
fdt_off_dt_struct(p) >= len ||
|
||||
fdt_off_dt_strings(p) >= len)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
const char *file;
|
||||
char *buf;
|
||||
bool debug = false;
|
||||
bool scan = false;
|
||||
size_t len;
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"**** fdtdump is a low-level debugging tool, not meant for general use.\n"
|
||||
"**** If you want to decompile a dtb, you probably want\n"
|
||||
"**** dtc -I dtb -O dts <filename>\n\n"
|
||||
);
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
switch (opt) {
|
||||
case_USAGE_COMMON_FLAGS
|
||||
|
||||
case 'd':
|
||||
debug = true;
|
||||
break;
|
||||
case 's':
|
||||
scan = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (optind != argc - 1)
|
||||
usage("missing input filename");
|
||||
file = argv[optind];
|
||||
|
||||
buf = utilfdt_read(file, &len);
|
||||
if (!buf)
|
||||
die("could not read: %s\n", file);
|
||||
|
||||
/* try and locate an embedded fdt in a bigger blob */
|
||||
if (scan) {
|
||||
unsigned char smagic[FDT_MAGIC_SIZE];
|
||||
char *p = buf;
|
||||
char *endp = buf + len;
|
||||
|
||||
fdt32_st(smagic, FDT_MAGIC);
|
||||
|
||||
/* poor man's memmem */
|
||||
while ((endp - p) >= FDT_MAGIC_SIZE) {
|
||||
p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
|
||||
if (!p)
|
||||
break;
|
||||
if (fdt_magic(p) == FDT_MAGIC) {
|
||||
/* try and validate the main struct */
|
||||
off_t this_len = endp - p;
|
||||
if (valid_header(p, this_len))
|
||||
break;
|
||||
if (debug)
|
||||
printf("%s: skipping fdt magic at offset %#tx\n",
|
||||
file, p - buf);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if (!p || (size_t)(endp - p) < sizeof(struct fdt_header))
|
||||
die("%s: could not locate fdt magic\n", file);
|
||||
printf("%s: found fdt at offset %#tx\n", file, p - buf);
|
||||
buf = p;
|
||||
} else if (!valid_header(buf, len))
|
||||
die("%s: header is not valid\n", file);
|
||||
|
||||
dump_blob(buf, debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
380
fdtget.c
380
fdtget.c
|
|
@ -1,380 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
||||
*
|
||||
* Portions from U-Boot cmd_fdt.c (C) Copyright 2007
|
||||
* Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
|
||||
* Based on code written by:
|
||||
* Pantelis Antoniou <pantelis.antoniou@gmail.com> and
|
||||
* Matthew McClintock <msm@freescale.com>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
enum display_mode {
|
||||
MODE_SHOW_VALUE, /* show values for node properties */
|
||||
MODE_LIST_PROPS, /* list the properties for a node */
|
||||
MODE_LIST_SUBNODES, /* list the subnodes of a node */
|
||||
};
|
||||
|
||||
/* Holds information which controls our output and options */
|
||||
struct display_info {
|
||||
int type; /* data type (s/i/u/x or 0 for default) */
|
||||
int size; /* data size (1/2/4) */
|
||||
enum display_mode mode; /* display mode that we are using */
|
||||
const char *default_val; /* default value if node/property not found */
|
||||
};
|
||||
|
||||
static void report_error(const char *where, int err)
|
||||
{
|
||||
fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a list of cells in the requested format
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param data Data to display
|
||||
* @param len Maximum length of buffer
|
||||
* @param size Data size to use for display (e.g. 4 for 32-bit)
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int show_cell_list(struct display_info *disp, const char *data, int len,
|
||||
int size)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)data;
|
||||
char fmt[3];
|
||||
int value;
|
||||
int i;
|
||||
|
||||
fmt[0] = '%';
|
||||
fmt[1] = disp->type ? disp->type : 'd';
|
||||
fmt[2] = '\0';
|
||||
for (i = 0; i < len; i += size, p += size) {
|
||||
if (i)
|
||||
printf(" ");
|
||||
switch (size) {
|
||||
case 4: value = fdt32_ld((const fdt32_t *)p); break;
|
||||
case 2: value = fdt16_ld((const fdt16_t *)p); break;
|
||||
case 1:
|
||||
default:
|
||||
value = *p;
|
||||
break;
|
||||
}
|
||||
printf(fmt, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays data of a given length according to selected options
|
||||
*
|
||||
* If a specific data type is provided in disp, then this is used. Otherwise
|
||||
* we try to guess the data type / size from the contents.
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param data Data to display
|
||||
* @param len Maximum length of buffer
|
||||
* @return 0 if ok, -1 if data does not match format
|
||||
*/
|
||||
static int show_data(struct display_info *disp, const char *data, int len)
|
||||
{
|
||||
int size;
|
||||
const char *s;
|
||||
int is_string;
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (disp->type == 'r') {
|
||||
fwrite(data, 1, len, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
is_string = (disp->type) == 's' ||
|
||||
(!disp->type && util_is_printable_string(data, len));
|
||||
if (is_string) {
|
||||
if (data[len - 1] != '\0') {
|
||||
fprintf(stderr, "Unterminated string\n");
|
||||
return -1;
|
||||
}
|
||||
for (s = data; s - data < len; s += strlen(s) + 1) {
|
||||
if (s != data)
|
||||
printf(" ");
|
||||
printf("%s", (const char *)s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size = disp->size;
|
||||
if (size == -1) {
|
||||
size = (len % 4) == 0 ? 4 : 1;
|
||||
} else if (len % size) {
|
||||
fprintf(stderr, "Property length must be a multiple of "
|
||||
"selected data size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return show_cell_list(disp, data, len, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all properties in a node, one per line.
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param node Node to display
|
||||
* @return 0 if ok, or FDT_ERR... if not.
|
||||
*/
|
||||
static int list_properties(const void *blob, int node)
|
||||
{
|
||||
const char *name;
|
||||
int prop;
|
||||
|
||||
prop = fdt_first_property_offset(blob, node);
|
||||
do {
|
||||
/* Stop silently when there are no more properties */
|
||||
if (prop < 0)
|
||||
return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
|
||||
fdt_getprop_by_offset(blob, prop, &name, NULL);
|
||||
if (name)
|
||||
puts(name);
|
||||
prop = fdt_next_property_offset(blob, prop);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
#define MAX_LEVEL 32 /* how deeply nested we will go */
|
||||
|
||||
/**
|
||||
* List all subnodes in a node, one per line
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param node Node to display
|
||||
* @return 0 if ok, or FDT_ERR... if not.
|
||||
*/
|
||||
static int list_subnodes(const void *blob, int node)
|
||||
{
|
||||
int nextoffset; /* next node offset from libfdt */
|
||||
uint32_t tag; /* current tag */
|
||||
int level = 0; /* keep track of nesting level */
|
||||
const char *pathp;
|
||||
int depth = 1; /* the assumed depth of this node */
|
||||
|
||||
while (level >= 0) {
|
||||
tag = fdt_next_tag(blob, node, &nextoffset);
|
||||
switch (tag) {
|
||||
case FDT_BEGIN_NODE:
|
||||
pathp = fdt_get_name(blob, node, NULL);
|
||||
if (level <= depth) {
|
||||
if (pathp == NULL)
|
||||
pathp = "/* NULL pointer error */";
|
||||
if (*pathp == '\0')
|
||||
pathp = "/"; /* root is nameless */
|
||||
if (level == 1)
|
||||
puts(pathp);
|
||||
}
|
||||
level++;
|
||||
if (level >= MAX_LEVEL) {
|
||||
printf("Nested too deep, aborting.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case FDT_END_NODE:
|
||||
level--;
|
||||
if (level == 0)
|
||||
level = -1; /* exit the loop */
|
||||
break;
|
||||
case FDT_END:
|
||||
return 1;
|
||||
case FDT_PROP:
|
||||
break;
|
||||
default:
|
||||
if (level <= depth)
|
||||
printf("Unknown tag 0x%08X\n", tag);
|
||||
return 1;
|
||||
}
|
||||
node = nextoffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the data for a given node (and perhaps property) according to the
|
||||
* display option provided.
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param disp Display information / options
|
||||
* @param node Node to display
|
||||
* @param property Name of property to display, or NULL if none
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
static int show_data_for_item(const void *blob, struct display_info *disp,
|
||||
int node, const char *property)
|
||||
{
|
||||
const void *value = NULL;
|
||||
int len, err = 0;
|
||||
|
||||
switch (disp->mode) {
|
||||
case MODE_LIST_PROPS:
|
||||
err = list_properties(blob, node);
|
||||
break;
|
||||
|
||||
case MODE_LIST_SUBNODES:
|
||||
err = list_subnodes(blob, node);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(property);
|
||||
value = fdt_getprop(blob, node, property, &len);
|
||||
if (value) {
|
||||
if (show_data(disp, value, len))
|
||||
err = -1;
|
||||
else
|
||||
printf("\n");
|
||||
} else if (disp->default_val) {
|
||||
puts(disp->default_val);
|
||||
} else {
|
||||
report_error(property, len);
|
||||
err = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the main fdtget operation, given a filename and valid arguments
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param filename Filename of blob file
|
||||
* @param arg List of arguments to process
|
||||
* @param arg_count Number of arguments
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
static int do_fdtget(struct display_info *disp, const char *filename,
|
||||
char **arg, int arg_count, int args_per_step)
|
||||
{
|
||||
char *blob;
|
||||
const char *prop;
|
||||
int i, node;
|
||||
|
||||
blob = utilfdt_read(filename, NULL);
|
||||
if (!blob)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i + args_per_step <= arg_count; i += args_per_step) {
|
||||
node = fdt_path_offset(blob, arg[i]);
|
||||
if (node < 0) {
|
||||
if (disp->default_val) {
|
||||
puts(disp->default_val);
|
||||
continue;
|
||||
} else {
|
||||
report_error(arg[i], node);
|
||||
free(blob);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
prop = args_per_step == 1 ? NULL : arg[i + 1];
|
||||
|
||||
if (show_data_for_item(blob, disp, node, prop)) {
|
||||
free(blob);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(blob);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] =
|
||||
"read values from device tree\n"
|
||||
" fdtget <options> <dt file> [<node> <property>]...\n"
|
||||
" fdtget -p <options> <dt file> [<node> ]...\n"
|
||||
"\n"
|
||||
"Each value is printed on a new line.\n"
|
||||
USAGE_TYPE_MSG;
|
||||
static const char usage_short_opts[] = "t:pld:" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"type", a_argument, NULL, 't'},
|
||||
{"properties", no_argument, NULL, 'p'},
|
||||
{"list", no_argument, NULL, 'l'},
|
||||
{"default", a_argument, NULL, 'd'},
|
||||
USAGE_COMMON_LONG_OPTS,
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"Type of data",
|
||||
"List properties for each node",
|
||||
"List subnodes for each node",
|
||||
"Default value to display when the property is missing",
|
||||
USAGE_COMMON_OPTS_HELP
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
char *filename = NULL;
|
||||
struct display_info disp;
|
||||
int args_per_step = 2;
|
||||
|
||||
/* set defaults */
|
||||
memset(&disp, '\0', sizeof(disp));
|
||||
disp.size = -1;
|
||||
disp.mode = MODE_SHOW_VALUE;
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
switch (opt) {
|
||||
case_USAGE_COMMON_FLAGS
|
||||
|
||||
case 't':
|
||||
if (utilfdt_decode_type(optarg, &disp.type,
|
||||
&disp.size))
|
||||
usage("invalid type string");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
disp.mode = MODE_LIST_PROPS;
|
||||
args_per_step = 1;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
disp.mode = MODE_LIST_SUBNODES;
|
||||
args_per_step = 1;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
disp.default_val = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filename = argv[optind++];
|
||||
if (!filename)
|
||||
usage("missing filename");
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
/* Allow no arguments, and silently succeed */
|
||||
if (!argc)
|
||||
return 0;
|
||||
|
||||
/* Check for node, property arguments */
|
||||
if (args_per_step == 2 && (argc % 2))
|
||||
usage("must have an even number of arguments");
|
||||
|
||||
if (do_fdtget(&disp, filename, argv, argc, args_per_step))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
214
fdtoverlay.c
214
fdtoverlay.c
|
|
@ -1,214 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2017 Konsulko Group Inc. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Pantelis Antoniou <pantelis.antoniou@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define BUF_INCREMENT 65536
|
||||
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] =
|
||||
"apply a number of overlays to a base blob\n"
|
||||
" fdtoverlay <options> [<overlay.dtbo> [<overlay.dtbo>]]";
|
||||
static const char usage_short_opts[] = "i:o:v" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"input", required_argument, NULL, 'i'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"verbose", no_argument, NULL, 'v'},
|
||||
USAGE_COMMON_LONG_OPTS,
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"Input base DT blob",
|
||||
"Output DT blob",
|
||||
"Verbose messages",
|
||||
USAGE_COMMON_OPTS_HELP
|
||||
};
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
static void *apply_one(char *base, const char *overlay, size_t *buf_len,
|
||||
const char *name)
|
||||
{
|
||||
char *tmp = NULL;
|
||||
char *tmpo;
|
||||
int ret;
|
||||
bool has_symbols;
|
||||
|
||||
/*
|
||||
* We take copies first, because a failed apply can trash
|
||||
* both the base blob and the overlay
|
||||
*/
|
||||
tmpo = xmalloc(fdt_totalsize(overlay));
|
||||
|
||||
do {
|
||||
tmp = xrealloc(tmp, *buf_len);
|
||||
ret = fdt_open_into(base, tmp, *buf_len);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"\nFailed to make temporary copy: %s\n",
|
||||
fdt_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
ret = fdt_path_offset(tmp, "/__symbols__");
|
||||
has_symbols = ret >= 0;
|
||||
|
||||
memcpy(tmpo, overlay, fdt_totalsize(overlay));
|
||||
|
||||
ret = fdt_overlay_apply(tmp, tmpo);
|
||||
if (ret == -FDT_ERR_NOSPACE) {
|
||||
*buf_len += BUF_INCREMENT;
|
||||
}
|
||||
} while (ret == -FDT_ERR_NOSPACE);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "\nFailed to apply '%s': %s\n",
|
||||
name, fdt_strerror(ret));
|
||||
if (!has_symbols) {
|
||||
fprintf(stderr,
|
||||
"base blob does not have a '/__symbols__' node, "
|
||||
"make sure you have compiled the base blob with '-@' option\n");
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(base);
|
||||
free(tmpo);
|
||||
return tmp;
|
||||
|
||||
fail:
|
||||
free(tmpo);
|
||||
if (tmp)
|
||||
free(tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
static int do_fdtoverlay(const char *input_filename,
|
||||
const char *output_filename,
|
||||
int argc, char *argv[])
|
||||
{
|
||||
char *blob = NULL;
|
||||
char **ovblob = NULL;
|
||||
size_t buf_len;
|
||||
int i, ret = -1;
|
||||
|
||||
blob = utilfdt_read(input_filename, &buf_len);
|
||||
if (!blob) {
|
||||
fprintf(stderr, "\nFailed to read '%s'\n", input_filename);
|
||||
goto out_err;
|
||||
}
|
||||
if (fdt_totalsize(blob) > buf_len) {
|
||||
fprintf(stderr,
|
||||
"\nBase blob is incomplete (%lu / %" PRIu32 " bytes read)\n",
|
||||
(unsigned long)buf_len, fdt_totalsize(blob));
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* allocate blob pointer array */
|
||||
ovblob = xmalloc(sizeof(*ovblob) * argc);
|
||||
memset(ovblob, 0, sizeof(*ovblob) * argc);
|
||||
|
||||
/* read and keep track of the overlay blobs */
|
||||
for (i = 0; i < argc; i++) {
|
||||
size_t ov_len;
|
||||
ovblob[i] = utilfdt_read(argv[i], &ov_len);
|
||||
if (!ovblob[i]) {
|
||||
fprintf(stderr, "\nFailed to read '%s'\n", argv[i]);
|
||||
goto out_err;
|
||||
}
|
||||
if (fdt_totalsize(ovblob[i]) > ov_len) {
|
||||
fprintf(stderr,
|
||||
"\nOverlay '%s' is incomplete (%lu / %" PRIu32 " bytes read)\n",
|
||||
argv[i], (unsigned long)ov_len,
|
||||
fdt_totalsize(ovblob[i]));
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
buf_len = fdt_totalsize(blob);
|
||||
|
||||
/* apply the overlays in sequence */
|
||||
for (i = 0; i < argc; i++) {
|
||||
blob = apply_one(blob, ovblob[i], &buf_len, argv[i]);
|
||||
if (!blob)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
fdt_pack(blob);
|
||||
ret = utilfdt_write(output_filename, blob);
|
||||
if (ret)
|
||||
fprintf(stderr, "\nFailed to write '%s'\n",
|
||||
output_filename);
|
||||
|
||||
out_err:
|
||||
if (ovblob) {
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (ovblob[i])
|
||||
free(ovblob[i]);
|
||||
}
|
||||
free(ovblob);
|
||||
}
|
||||
free(blob);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt, i;
|
||||
char *input_filename = NULL;
|
||||
char *output_filename = NULL;
|
||||
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
switch (opt) {
|
||||
case_USAGE_COMMON_FLAGS
|
||||
|
||||
case 'i':
|
||||
input_filename = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
output_filename = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_filename)
|
||||
usage("missing input file");
|
||||
|
||||
if (!output_filename)
|
||||
usage("missing output file");
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (argc <= 0)
|
||||
usage("missing overlay file(s)");
|
||||
|
||||
if (verbose) {
|
||||
printf("input = %s\n", input_filename);
|
||||
printf("output = %s\n", output_filename);
|
||||
for (i = 0; i < argc; i++)
|
||||
printf("overlay[%d] = %s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
if (do_fdtoverlay(input_filename, output_filename, argc, argv))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
494
fdtput.c
494
fdtput.c
|
|
@ -1,494 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* These are the operations we support */
|
||||
enum oper_type {
|
||||
OPER_WRITE_PROP, /* Write a property in a node */
|
||||
OPER_CREATE_NODE, /* Create a new node */
|
||||
OPER_REMOVE_NODE, /* Delete a node */
|
||||
OPER_DELETE_PROP, /* Delete a property in a node */
|
||||
};
|
||||
|
||||
struct display_info {
|
||||
enum oper_type oper; /* operation to perform */
|
||||
int type; /* data type (s/i/u/x or 0 for default) */
|
||||
int size; /* data size (1/2/4) */
|
||||
int verbose; /* verbose output */
|
||||
int auto_path; /* automatically create all path components */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Report an error with a particular node.
|
||||
*
|
||||
* @param name Node name to report error on
|
||||
* @param namelen Length of node name, or -1 to use entire string
|
||||
* @param err Error number to report (-FDT_ERR_...)
|
||||
*/
|
||||
static void report_error(const char *name, int namelen, int err)
|
||||
{
|
||||
if (namelen == -1)
|
||||
namelen = strlen(name);
|
||||
fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
|
||||
fdt_strerror(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a series of arguments in a property value.
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param arg List of arguments from command line
|
||||
* @param arg_count Number of arguments (may be 0)
|
||||
* @param valuep Returns buffer containing value
|
||||
* @param value_len Returns length of value encoded
|
||||
*/
|
||||
static int encode_value(struct display_info *disp, char **arg, int arg_count,
|
||||
char **valuep, int *value_len)
|
||||
{
|
||||
char *value = NULL; /* holding area for value */
|
||||
int value_size = 0; /* size of holding area */
|
||||
char *ptr; /* pointer to current value position */
|
||||
int len; /* length of this cell/string/byte */
|
||||
int ival;
|
||||
int upto; /* the number of bytes we have written to buf */
|
||||
|
||||
upto = 0;
|
||||
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "Decoding value:\n");
|
||||
|
||||
for (; arg_count > 0; arg++, arg_count--, upto += len) {
|
||||
/* assume integer unless told otherwise */
|
||||
if (disp->type == 's')
|
||||
len = strlen(*arg) + 1;
|
||||
else
|
||||
len = disp->size == -1 ? 4 : disp->size;
|
||||
|
||||
/* enlarge our value buffer by a suitable margin if needed */
|
||||
if (upto + len > value_size) {
|
||||
value_size = (upto + len) + 500;
|
||||
value = xrealloc(value, value_size);
|
||||
}
|
||||
|
||||
ptr = value + upto;
|
||||
if (disp->type == 's') {
|
||||
memcpy(ptr, *arg, len);
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "\tstring: '%s'\n", ptr);
|
||||
} else {
|
||||
fdt32_t *iptr = (fdt32_t *)ptr;
|
||||
char *endptr;
|
||||
|
||||
errno = 0;
|
||||
switch (disp->type) {
|
||||
case 'x':
|
||||
ival = strtoul(*arg, &endptr, 16);
|
||||
break;
|
||||
case 'o':
|
||||
ival = strtoul(*arg, &endptr, 8);
|
||||
break;
|
||||
case 'i':
|
||||
ival = strtol(*arg, &endptr, 0);
|
||||
break;
|
||||
case 'u':
|
||||
ival = strtoul(*arg, &endptr, 0);
|
||||
break;
|
||||
default: /* 0 or 'd' */
|
||||
ival = strtol(*arg, &endptr, 10);
|
||||
}
|
||||
|
||||
if (*endptr != '\0' || errno) {
|
||||
if (disp->verbose) {
|
||||
fprintf(stderr,
|
||||
"Couldn't parse \"%s\" as an integer\n",
|
||||
*arg);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (len == 4)
|
||||
*iptr = cpu_to_fdt32(ival);
|
||||
else
|
||||
*ptr = (uint8_t)ival;
|
||||
if (disp->verbose) {
|
||||
fprintf(stderr, "\t%s: %d\n",
|
||||
disp->size == 1 ? "byte" :
|
||||
disp->size == 2 ? "short" : "int",
|
||||
ival);
|
||||
}
|
||||
}
|
||||
}
|
||||
*value_len = upto;
|
||||
*valuep = value;
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "Value size %d\n", upto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
|
||||
|
||||
static char *realloc_fdt(char *fdt, int delta)
|
||||
{
|
||||
int new_sz = fdt_totalsize(fdt) + delta;
|
||||
fdt = xrealloc(fdt, new_sz);
|
||||
fdt_open_into(fdt, fdt, new_sz);
|
||||
return fdt;
|
||||
}
|
||||
|
||||
static char *realloc_node(char *fdt, const char *name)
|
||||
{
|
||||
int delta;
|
||||
/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
|
||||
delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
|
||||
+ FDT_TAGSIZE;
|
||||
return realloc_fdt(fdt, delta);
|
||||
}
|
||||
|
||||
static char *realloc_property(char *fdt, int nodeoffset,
|
||||
const char *name, int newlen)
|
||||
{
|
||||
int delta = 0;
|
||||
int oldlen = 0;
|
||||
|
||||
if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
|
||||
/* strings + property header */
|
||||
delta = sizeof(struct fdt_property) + strlen(name) + 1;
|
||||
|
||||
if (newlen > oldlen)
|
||||
/* actual value in off_struct */
|
||||
delta += ALIGN(newlen) - ALIGN(oldlen);
|
||||
|
||||
return realloc_fdt(fdt, delta);
|
||||
}
|
||||
|
||||
static int store_key_value(char **blob, const char *node_name,
|
||||
const char *property, const char *buf, int len)
|
||||
{
|
||||
int node;
|
||||
int err;
|
||||
|
||||
node = fdt_path_offset(*blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = fdt_setprop(*blob, node, property, buf, len);
|
||||
if (err == -FDT_ERR_NOSPACE) {
|
||||
*blob = realloc_property(*blob, node, property, len);
|
||||
err = fdt_setprop(*blob, node, property, buf, len);
|
||||
}
|
||||
if (err) {
|
||||
report_error(property, -1, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create paths as needed for all components of a path
|
||||
*
|
||||
* Any components of the path that do not exist are created. Errors are
|
||||
* reported.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param in_path Path to process
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int create_paths(char **blob, const char *in_path)
|
||||
{
|
||||
const char *path = in_path;
|
||||
const char *sep;
|
||||
int node, offset = 0;
|
||||
|
||||
/* skip leading '/' */
|
||||
while (*path == '/')
|
||||
path++;
|
||||
|
||||
for (sep = path; *sep; path = sep + 1, offset = node) {
|
||||
/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
|
||||
sep = strchr(path, '/');
|
||||
if (!sep)
|
||||
sep = path + strlen(path);
|
||||
|
||||
node = fdt_subnode_offset_namelen(*blob, offset, path,
|
||||
sep - path);
|
||||
if (node == -FDT_ERR_NOTFOUND) {
|
||||
*blob = realloc_node(*blob, path);
|
||||
node = fdt_add_subnode_namelen(*blob, offset, path,
|
||||
sep - path);
|
||||
}
|
||||
if (node < 0) {
|
||||
report_error(path, sep - path, node);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new node in the fdt.
|
||||
*
|
||||
* This will overwrite the node_name string. Any error is reported.
|
||||
*
|
||||
* TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Name of node to create
|
||||
* @return new node offset if found, or -1 on failure
|
||||
*/
|
||||
static int create_node(char **blob, const char *node_name)
|
||||
{
|
||||
int node = 0;
|
||||
const char *p;
|
||||
char *path = NULL;
|
||||
|
||||
p = strrchr(node_name, '/');
|
||||
if (!p) {
|
||||
report_error(node_name, -1, -FDT_ERR_BADPATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*blob = realloc_node(*blob, p + 1);
|
||||
|
||||
if (p > node_name) {
|
||||
path = xstrndup(node_name, (size_t)(p - node_name));
|
||||
node = fdt_path_offset(*blob, path);
|
||||
free(path);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
node = fdt_add_subnode(*blob, node, p + 1);
|
||||
if (node < 0) {
|
||||
report_error(p + 1, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a property of a node in the fdt.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Path to node containing the property to delete
|
||||
* @param prop_name Name of property to delete
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
static int delete_prop(char *blob, const char *node_name, const char *prop_name)
|
||||
{
|
||||
int node = 0;
|
||||
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
node = fdt_delprop(blob, node, prop_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a node in the fdt.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Name of node to delete
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
static int delete_node(char *blob, const char *node_name)
|
||||
{
|
||||
int node = 0;
|
||||
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
node = fdt_del_node(blob, node);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_fdtput(struct display_info *disp, const char *filename,
|
||||
char **arg, int arg_count)
|
||||
{
|
||||
char *value = NULL;
|
||||
char *blob;
|
||||
char *node;
|
||||
int len, ret = 0;
|
||||
|
||||
blob = utilfdt_read(filename, NULL);
|
||||
if (!blob)
|
||||
return -1;
|
||||
|
||||
switch (disp->oper) {
|
||||
case OPER_WRITE_PROP:
|
||||
/*
|
||||
* Convert the arguments into a single binary value, then
|
||||
* store them into the property.
|
||||
*/
|
||||
assert(arg_count >= 2);
|
||||
if (disp->auto_path && create_paths(&blob, *arg))
|
||||
return -1;
|
||||
if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
|
||||
store_key_value(&blob, *arg, arg[1], value, len))
|
||||
ret = -1;
|
||||
break;
|
||||
case OPER_CREATE_NODE:
|
||||
for (; ret >= 0 && arg_count--; arg++) {
|
||||
if (disp->auto_path)
|
||||
ret = create_paths(&blob, *arg);
|
||||
else
|
||||
ret = create_node(&blob, *arg);
|
||||
}
|
||||
break;
|
||||
case OPER_REMOVE_NODE:
|
||||
for (; ret >= 0 && arg_count--; arg++)
|
||||
ret = delete_node(blob, *arg);
|
||||
break;
|
||||
case OPER_DELETE_PROP:
|
||||
node = *arg;
|
||||
for (arg++; ret >= 0 && arg_count-- > 1; arg++)
|
||||
ret = delete_prop(blob, node, *arg);
|
||||
break;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
fdt_pack(blob);
|
||||
ret = utilfdt_write(filename, blob);
|
||||
}
|
||||
|
||||
free(blob);
|
||||
|
||||
if (value) {
|
||||
free(value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
static const char usage_synopsis[] =
|
||||
"write a property value to a device tree\n"
|
||||
" fdtput <options> <dt file> <node> <property> [<value>...]\n"
|
||||
" fdtput -c <options> <dt file> [<node>...]\n"
|
||||
" fdtput -r <options> <dt file> [<node>...]\n"
|
||||
" fdtput -d <options> <dt file> <node> [<property>...]\n"
|
||||
"\n"
|
||||
"The command line arguments are joined together into a single value.\n"
|
||||
USAGE_TYPE_MSG;
|
||||
static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"create", no_argument, NULL, 'c'},
|
||||
{"remove", no_argument, NULL, 'r'},
|
||||
{"delete", no_argument, NULL, 'd'},
|
||||
{"auto-path", no_argument, NULL, 'p'},
|
||||
{"type", a_argument, NULL, 't'},
|
||||
{"verbose", no_argument, NULL, 'v'},
|
||||
USAGE_COMMON_LONG_OPTS,
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"Create nodes",
|
||||
"Delete nodes (and any subnodes)",
|
||||
"Delete properties if they already exist",
|
||||
"Automatically create nodes as needed for the node path",
|
||||
"Type of data",
|
||||
"Display each value decoded from command line",
|
||||
USAGE_COMMON_OPTS_HELP
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
struct display_info disp;
|
||||
char *filename = NULL;
|
||||
|
||||
memset(&disp, '\0', sizeof(disp));
|
||||
disp.size = -1;
|
||||
disp.oper = OPER_WRITE_PROP;
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
/*
|
||||
* TODO: add options to:
|
||||
* - rename node
|
||||
* - pack fdt before writing
|
||||
* - set amount of free space when writing
|
||||
*/
|
||||
switch (opt) {
|
||||
case_USAGE_COMMON_FLAGS
|
||||
|
||||
case 'c':
|
||||
disp.oper = OPER_CREATE_NODE;
|
||||
break;
|
||||
case 'r':
|
||||
disp.oper = OPER_REMOVE_NODE;
|
||||
break;
|
||||
case 'd':
|
||||
disp.oper = OPER_DELETE_PROP;
|
||||
break;
|
||||
case 'p':
|
||||
disp.auto_path = 1;
|
||||
break;
|
||||
case 't':
|
||||
if (utilfdt_decode_type(optarg, &disp.type,
|
||||
&disp.size))
|
||||
usage("Invalid type string");
|
||||
if (disp.type == 'r')
|
||||
usage("Unsupported raw data type");
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
disp.verbose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filename = argv[optind++];
|
||||
if (!filename)
|
||||
usage("missing filename");
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (disp.oper == OPER_WRITE_PROP) {
|
||||
if (argc < 1)
|
||||
usage("missing node");
|
||||
if (argc < 2)
|
||||
usage("missing property");
|
||||
}
|
||||
|
||||
if (disp.oper == OPER_DELETE_PROP)
|
||||
if (argc < 1)
|
||||
usage("missing node");
|
||||
|
||||
if (do_fdtput(&disp, filename, argv, argc))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
45
flat_dt.h
Normal file
45
flat_dt.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef _FLAT_DT_H_
|
||||
#define _FLAT_DT_H_
|
||||
|
||||
|
||||
#define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */
|
||||
|
||||
#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name */
|
||||
#define OF_DT_END_NODE 0x2 /* End node */
|
||||
#define OF_DT_PROP 0x3 /* Property: name off,
|
||||
size, content */
|
||||
#define OF_DT_NOP 0x4 /* nop */
|
||||
#define OF_DT_END 0x9
|
||||
|
||||
struct boot_param_header {
|
||||
uint32_t magic; /* magic word OF_DT_HEADER */
|
||||
uint32_t totalsize; /* total size of DT block */
|
||||
uint32_t off_dt_struct; /* offset to structure */
|
||||
uint32_t off_dt_strings; /* offset to strings */
|
||||
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
uint32_t version; /* format version */
|
||||
uint32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
uint32_t size_dt_strings; /* size of the strings block */
|
||||
};
|
||||
|
||||
#define BPH_V1_SIZE (7*sizeof(uint32_t))
|
||||
#define BPH_V2_SIZE (BPH_V1_SIZE + sizeof(uint32_t))
|
||||
#define BPH_V3_SIZE (BPH_V2_SIZE + sizeof(uint32_t))
|
||||
|
||||
struct reserve_entry {
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct flat_dt_property {
|
||||
uint32_t nameoff;
|
||||
uint32_t len;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#endif /* _FLAT_DT_H_ */
|
||||
673
flattree.c
673
flattree.c
File diff suppressed because it is too large
Load diff
58
fstree.c
58
fstree.c
|
|
@ -1,6 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
|
@ -8,7 +23,7 @@
|
|||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static struct node *read_fstree(const char *dirname)
|
||||
static struct node *read_fstree(char *dirname)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
|
|
@ -16,34 +31,34 @@ static struct node *read_fstree(const char *dirname)
|
|||
struct node *tree;
|
||||
|
||||
d = opendir(dirname);
|
||||
if (!d)
|
||||
die("Couldn't opendir() \"%s\": %s\n", dirname, strerror(errno));
|
||||
if (! d)
|
||||
die("opendir(): %s\n", strerror(errno));
|
||||
|
||||
tree = build_node(NULL, NULL, NULL);
|
||||
tree = build_node(NULL, NULL);
|
||||
|
||||
while ((de = readdir(d)) != NULL) {
|
||||
char *tmpname;
|
||||
char *tmpnam;
|
||||
|
||||
if (streq(de->d_name, ".")
|
||||
|| streq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
tmpname = join_path(dirname, de->d_name);
|
||||
|
||||
if (stat(tmpname, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpname, strerror(errno));
|
||||
tmpnam = join_path(dirname, de->d_name);
|
||||
|
||||
if (lstat(tmpnam, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpnam, strerror(errno));
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
struct property *prop;
|
||||
FILE *pfile;
|
||||
|
||||
pfile = fopen(tmpname, "rb");
|
||||
pfile = fopen(tmpnam, "r");
|
||||
if (! pfile) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Cannot open %s: %s\n",
|
||||
tmpname, strerror(errno));
|
||||
tmpnam, strerror(errno));
|
||||
} else {
|
||||
prop = build_property(de->d_name,
|
||||
prop = build_property(strdup(de->d_name),
|
||||
data_copy_file(pfile,
|
||||
st.st_size),
|
||||
NULL);
|
||||
|
|
@ -53,24 +68,27 @@ static struct node *read_fstree(const char *dirname)
|
|||
} else if (S_ISDIR(st.st_mode)) {
|
||||
struct node *newchild;
|
||||
|
||||
newchild = read_fstree(tmpname);
|
||||
newchild = name_node(newchild, xstrdup(de->d_name));
|
||||
newchild = read_fstree(tmpnam);
|
||||
newchild = name_node(newchild, strdup(de->d_name),
|
||||
NULL);
|
||||
add_child(tree, newchild);
|
||||
}
|
||||
|
||||
free(tmpname);
|
||||
free(tmpnam);
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return tree;
|
||||
}
|
||||
|
||||
struct dt_info *dt_from_fs(const char *dirname)
|
||||
struct boot_info *dt_from_fs(char *dirname)
|
||||
{
|
||||
struct node *tree;
|
||||
|
||||
tree = read_fstree(dirname);
|
||||
tree = name_node(tree, "");
|
||||
tree = name_node(tree, "", NULL);
|
||||
|
||||
return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
|
||||
fill_fullpaths(tree, "");
|
||||
|
||||
return build_boot_info(NULL, tree);
|
||||
}
|
||||
|
||||
|
|
|
|||
188
ftdump.c
Normal file
188
ftdump.c
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* ftdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <netinet/in.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#include "flat_dt.h"
|
||||
|
||||
#define cpu_to_be16(x) htons(x)
|
||||
#define be16_to_cpu(x) ntohs(x)
|
||||
|
||||
#define cpu_to_be32(x) htonl(x)
|
||||
#define be32_to_cpu(x) ntohl(x)
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define cpu_to_be64(x) (x)
|
||||
#define be64_to_cpu(x) (x)
|
||||
#else
|
||||
#define cpu_to_be64(x) bswap_64(x)
|
||||
#define be64_to_cpu(x) bswap_64(x)
|
||||
#endif
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
|
||||
#define GET_CELL(p) (p += 4, *((uint32_t *)(p-4)))
|
||||
|
||||
static int is_printable_string(const void *data, int len)
|
||||
{
|
||||
const char *s = data;
|
||||
const char *ss;
|
||||
|
||||
/* zero length is not */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
ss = s;
|
||||
while (*s && isprint(*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s != '\0' || (s + 1 - ss) < len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_data(const void *data, int len)
|
||||
{
|
||||
int i;
|
||||
const uint8_t *s;
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (is_printable_string(data, len)) {
|
||||
printf(" = \"%s\"", (char *)data);
|
||||
} else if ((len % 4) == 0) {
|
||||
printf(" = <");
|
||||
for (i = 0; i < len; i += 4)
|
||||
printf("%08x%s", *((uint32_t *)data + i),
|
||||
i < (len - 4) ? " " : "");
|
||||
printf(">");
|
||||
} else {
|
||||
printf(" = [");
|
||||
for (i = 0, s = data; i < len; i++)
|
||||
printf("%02x%s", s[i], i < len - 1 ? " " : "");
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_blob(void *blob)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
struct reserve_entry *p_rsvmap =
|
||||
(struct reserve_entry *)(blob
|
||||
+ be32_to_cpu(bph->off_mem_rsvmap));
|
||||
char *p_struct = blob + be32_to_cpu(bph->off_dt_struct);
|
||||
char *p_strings = blob + be32_to_cpu(bph->off_dt_strings);
|
||||
uint32_t version = be32_to_cpu(bph->version);
|
||||
uint32_t tag;
|
||||
char *p;
|
||||
char *s, *t;
|
||||
int depth, sz, shift;
|
||||
int i;
|
||||
uint64_t addr, size;
|
||||
|
||||
depth = 0;
|
||||
shift = 4;
|
||||
|
||||
printf("// Version 0x%x tree\n", version);
|
||||
for (i = 0; ; i++) {
|
||||
addr = be64_to_cpu(p_rsvmap[i].address);
|
||||
size = be64_to_cpu(p_rsvmap[i].size);
|
||||
if (addr == 0 && size == 0)
|
||||
break;
|
||||
|
||||
printf("/memreserve/ %llx %llx;\n",
|
||||
(unsigned long long)addr, (unsigned long long)size);
|
||||
}
|
||||
|
||||
p = p_struct;
|
||||
while ((tag = be32_to_cpu(GET_CELL(p))) != OF_DT_END) {
|
||||
|
||||
/* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */
|
||||
|
||||
if (tag == OF_DT_BEGIN_NODE) {
|
||||
s = p;
|
||||
p = PALIGN(p + strlen(s) + 1, 4);
|
||||
|
||||
if (*s == '\0')
|
||||
s = "/";
|
||||
|
||||
printf("%*s%s {\n", depth * shift, "", s);
|
||||
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == OF_DT_END_NODE) {
|
||||
depth--;
|
||||
|
||||
printf("%*s};\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == OF_DT_NOP) {
|
||||
printf("%*s// [NOP]\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag != OF_DT_PROP) {
|
||||
fprintf(stderr, "%*s ** Unknown tag 0x%08x\n", depth * shift, "", tag);
|
||||
break;
|
||||
}
|
||||
sz = GET_CELL(p);
|
||||
s = p_strings + be32_to_cpu(GET_CELL(p));
|
||||
if (version < 0x10 && sz >= 8)
|
||||
p = PALIGN(p, 8);
|
||||
t = p;
|
||||
|
||||
p = PALIGN(p + sz, 4);
|
||||
|
||||
printf("%*s%s", depth * shift, "", s);
|
||||
print_data(t, sz);
|
||||
printf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[16384]; /* 16k max */
|
||||
int size;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "supply input filename\n");
|
||||
return 5;
|
||||
}
|
||||
|
||||
fp = fopen(argv[1], "rb");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "unable to open %s\n", argv[1]);
|
||||
return 10;
|
||||
}
|
||||
|
||||
size = fread(buf, 1, sizeof(buf), fp);
|
||||
if (size == sizeof(buf)) { /* too large */
|
||||
fprintf(stderr, "file too large\n");
|
||||
return 10;
|
||||
}
|
||||
|
||||
dump_blob(buf);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
209
libdt.c
Normal file
209
libdt.c
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "flat_dt.h"
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
|
||||
#define GET_CELL(p) (p += 4, *((uint32_t *)(p-4)))
|
||||
|
||||
static char *skip_name(char *p)
|
||||
{
|
||||
while (*p != '\0')
|
||||
p++;
|
||||
|
||||
return PALIGN(p, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static char *skip_prop(void *blob, char *p)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
uint32_t len, nameoff;
|
||||
|
||||
len = GET_CELL(p);
|
||||
nameoff = GET_CELL(p);
|
||||
if ((bph->version < 0x10) && (len >= sizeof(uint64_t)))
|
||||
p = PALIGN(p, sizeof(uint64_t));
|
||||
return PALIGN(p + len, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static char *get_unit(char *dtpath)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (dtpath[0] != '/')
|
||||
return dtpath;
|
||||
|
||||
p = dtpath + strlen(dtpath);
|
||||
while (*p != '/')
|
||||
p--;
|
||||
|
||||
return p+1;
|
||||
}
|
||||
|
||||
static int first_seg_len(char *dtpath)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
while ((dtpath[len] != '/') && (dtpath[len] != '\0'))
|
||||
len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
char *flat_dt_get_string(void *blob, uint32_t offset)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
|
||||
return (char *)blob + bph->off_dt_strings + offset;
|
||||
}
|
||||
|
||||
void *flat_dt_get_subnode(void *blob, void *node, char *uname, int unamelen)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
char *p = node;
|
||||
uint32_t tag;
|
||||
int depth = 0;
|
||||
char *nuname;
|
||||
|
||||
if (! unamelen)
|
||||
unamelen = strlen(uname);
|
||||
|
||||
do {
|
||||
tag = GET_CELL(p);
|
||||
|
||||
switch (tag) {
|
||||
case OF_DT_PROP:
|
||||
p = skip_prop(blob, p);
|
||||
break;
|
||||
|
||||
case OF_DT_BEGIN_NODE:
|
||||
if (depth == 0) {
|
||||
nuname = p;
|
||||
|
||||
if (bph->version < 0x10)
|
||||
nuname = get_unit(nuname);
|
||||
|
||||
p = skip_name(p);
|
||||
|
||||
if (strncmp(nuname, uname, unamelen) == 0)
|
||||
return p;
|
||||
}
|
||||
depth++;
|
||||
break;
|
||||
|
||||
case OF_DT_END_NODE:
|
||||
depth--;
|
||||
break;
|
||||
|
||||
case OF_DT_END:
|
||||
/* looks like a malformed tree */
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* FIXME: throw some sort of error */
|
||||
return NULL;
|
||||
}
|
||||
} while (depth >= 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *flat_dt_get_node(void *blob, char *path)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
char *node;
|
||||
int seglen;
|
||||
|
||||
node = blob + bph->off_dt_struct;
|
||||
node += sizeof(uint32_t); /* skip initial OF_DT_BEGIN_NODE */
|
||||
node = skip_name(node); /* skip root node name */
|
||||
|
||||
while (node && (*path)) {
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
|
||||
seglen = first_seg_len(path);
|
||||
|
||||
node = flat_dt_get_subnode(blob, node, path, seglen);
|
||||
|
||||
path += seglen;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void flat_dt_traverse(void *blob, int (*fn)(void *blob, void *node, void *priv),
|
||||
void *private)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
char *p;
|
||||
uint32_t tag;
|
||||
int depth = 0;
|
||||
char *uname;
|
||||
|
||||
p = (char *)blob + bph->off_dt_struct;
|
||||
|
||||
tag = GET_CELL(p);
|
||||
while (tag != OF_DT_END) {
|
||||
switch (tag) {
|
||||
case OF_DT_BEGIN_NODE:
|
||||
uname = p;
|
||||
|
||||
if (bph->version < 0x10)
|
||||
uname = get_unit(uname);
|
||||
|
||||
p = skip_name(p);
|
||||
|
||||
(*fn)(blob, p, private);
|
||||
depth++;
|
||||
break;
|
||||
|
||||
case OF_DT_END_NODE:
|
||||
depth--;
|
||||
break;
|
||||
|
||||
case OF_DT_PROP:
|
||||
p = skip_prop(blob, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* FIXME: badly formed tree */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *flat_dt_get_prop(void *blob, void *node, char *name, uint32_t *len)
|
||||
{
|
||||
struct boot_param_header *bph = blob;
|
||||
char *p = node;
|
||||
|
||||
do {
|
||||
uint32_t tag = GET_CELL(p);
|
||||
uint32_t sz, noff;
|
||||
const char *nstr;
|
||||
|
||||
if (tag != OF_DT_PROP)
|
||||
return NULL;
|
||||
|
||||
sz = GET_CELL(p);
|
||||
noff = GET_CELL(p);
|
||||
|
||||
/* Old versions have variable alignment of the
|
||||
* property value */
|
||||
if ((bph->version < 0x10) && (sz >= 8))
|
||||
p = PALIGN(p, 8);
|
||||
|
||||
nstr = flat_dt_get_string(blob, noff);
|
||||
|
||||
if (strcmp(name, nstr) == 0) {
|
||||
if (len)
|
||||
*len = sz;
|
||||
return (void *)p;
|
||||
}
|
||||
|
||||
p = PALIGN(p + sz, sizeof(uint32_t));
|
||||
} while(1);
|
||||
}
|
||||
1
libfdt/.gitignore
vendored
1
libfdt/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
libfdt.so.1
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
# Makefile.libfdt
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
|
||||
LIBFDT_so = libfdt.$(SHAREDLIB_EXT)
|
||||
LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
|
||||
LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
|
||||
LIBFDT_VERSION = version.lds
|
||||
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
|
||||
fdt_addresses.c fdt_overlay.c fdt_check.c
|
||||
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
|
||||
LIBFDT_LIB = libfdt.$(SHAREDLIB_EXT).$(DTC_VERSION)
|
||||
|
||||
libfdt_clean:
|
||||
@$(VECHO) CLEAN "(libfdt)"
|
||||
rm -f $(STD_CLEANFILES:%=$(LIBFDT_dir)/%)
|
||||
rm -f $(LIBFDT_dir)/$(LIBFDT_so)
|
||||
rm -f $(LIBFDT_dir)/$(LIBFDT_soname)
|
||||
rm -f $(LIBFDT_dir)/$(LIBFDT_LIB)
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
- Tree traversal functions
|
||||
- Graft function
|
||||
339
libfdt/fdt.c
339
libfdt/fdt.c
|
|
@ -1,339 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int32_t fdt_ro_probe_(const void *fdt)
|
||||
{
|
||||
uint32_t totalsize = fdt_totalsize(fdt);
|
||||
|
||||
if (can_assume(VALID_DTB))
|
||||
return totalsize;
|
||||
|
||||
/* The device tree must be at an 8-byte aligned address */
|
||||
if ((uintptr_t)fdt & 7)
|
||||
return -FDT_ERR_ALIGNMENT;
|
||||
|
||||
if (fdt_magic(fdt) == FDT_MAGIC) {
|
||||
/* Complete tree */
|
||||
if (!can_assume(LATEST)) {
|
||||
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 (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
|
||||
return -FDT_ERR_BADSTATE;
|
||||
} else {
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
}
|
||||
|
||||
if (totalsize < INT32_MAX)
|
||||
return totalsize;
|
||||
else
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t fdt_header_size(const void *fdt)
|
||||
{
|
||||
return can_assume(LATEST) ? FDT_V17_SIZE :
|
||||
fdt_header_size_(fdt_version(fdt));
|
||||
}
|
||||
|
||||
int fdt_check_header(const void *fdt)
|
||||
{
|
||||
size_t hdrsize;
|
||||
|
||||
/* The device tree must be at an 8-byte aligned address */
|
||||
if ((uintptr_t)fdt & 7)
|
||||
return -FDT_ERR_ALIGNMENT;
|
||||
|
||||
if (fdt_magic(fdt) != FDT_MAGIC)
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
if (!can_assume(LATEST)) {
|
||||
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;
|
||||
}
|
||||
hdrsize = fdt_header_size(fdt);
|
||||
if (!can_assume(VALID_DTB)) {
|
||||
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 (!can_assume(LATEST) && 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 int uoffset = offset;
|
||||
unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
|
||||
if (offset < 0)
|
||||
return NULL;
|
||||
|
||||
if (!can_assume(VALID_INPUT))
|
||||
if ((absoffset < uoffset)
|
||||
|| ((absoffset + len) < absoffset)
|
||||
|| (absoffset + len) > fdt_totalsize(fdt))
|
||||
return NULL;
|
||||
|
||||
if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
|
||||
if (((uoffset + len) < uoffset)
|
||||
|| ((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, len, sum;
|
||||
int offset = startoffset;
|
||||
const char *p;
|
||||
|
||||
*nextoffset = -FDT_ERR_TRUNCATED;
|
||||
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
|
||||
if (!can_assume(VALID_DTB) && !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 (!can_assume(VALID_DTB) && !p)
|
||||
return FDT_END; /* premature end */
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
|
||||
if (!can_assume(VALID_DTB) && !lenp)
|
||||
return FDT_END; /* premature end */
|
||||
|
||||
len = fdt32_to_cpu(*lenp);
|
||||
sum = len + offset;
|
||||
if (!can_assume(VALID_DTB) &&
|
||||
(INT_MAX <= sum || sum < (uint32_t) offset))
|
||||
return FDT_END; /* premature end */
|
||||
|
||||
/* skip-name offset, length and value */
|
||||
offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;
|
||||
|
||||
if (!can_assume(LATEST) &&
|
||||
fdt_version(fdt) < 0x10 && len >= 8 &&
|
||||
((offset - len) % 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 (!can_assume(VALID_INPUT)
|
||||
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
if (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 (!can_assume(VALID_INPUT)
|
||||
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
if (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_len_(const char *strtab, int tabsize, const char *s,
|
||||
int slen)
|
||||
{
|
||||
const char *last = strtab + tabsize - (slen + 1);
|
||||
const char *p;
|
||||
|
||||
for (p = strtab; p <= last; p++)
|
||||
if (memcmp(p, s, slen) == 0 && p[slen] == '\0')
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_move(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
if (!can_assume(VALID_INPUT) && bufsize < 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
FDT_RO_PROBE(fdt);
|
||||
|
||||
if (fdt_totalsize(fdt) > (unsigned int)bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memmove(buf, fdt, fdt_totalsize(fdt));
|
||||
return 0;
|
||||
}
|
||||
66
libfdt/fdt.h
66
libfdt/fdt.h
|
|
@ -1,66 +0,0 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
|
||||
#ifndef FDT_H
|
||||
#define FDT_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
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[];
|
||||
};
|
||||
|
||||
struct fdt_property {
|
||||
fdt32_t tag;
|
||||
fdt32_t len;
|
||||
fdt32_t nameoff;
|
||||
char data[];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#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 */
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*/
|
||||
#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;
|
||||
uint32_t val;
|
||||
int len;
|
||||
|
||||
c = fdt_getprop(fdt, nodeoffset, name, &len);
|
||||
if (!c)
|
||||
return len;
|
||||
|
||||
if (len != sizeof(*c))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
val = fdt32_to_cpu(*c);
|
||||
if (val > FDT_MAX_NCELLS)
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
return (int)val;
|
||||
}
|
||||
|
||||
int fdt_address_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fdt_cells(fdt, nodeoffset, "#address-cells");
|
||||
if (val == 0)
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
if (val == -FDT_ERR_NOTFOUND)
|
||||
return 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
int fdt_size_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = fdt_cells(fdt, nodeoffset, "#size-cells");
|
||||
if (val == -FDT_ERR_NOTFOUND)
|
||||
return 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* This function assumes that [address|size]_cells is 1 or 2 */
|
||||
int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
|
||||
const char *name, uint64_t addr, uint64_t size)
|
||||
{
|
||||
int addr_cells, size_cells, ret;
|
||||
uint8_t data[sizeof(fdt64_t) * 2], *prop;
|
||||
|
||||
ret = fdt_address_cells(fdt, parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
addr_cells = ret;
|
||||
|
||||
ret = fdt_size_cells(fdt, parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
size_cells = ret;
|
||||
|
||||
/* check validity of address */
|
||||
prop = data;
|
||||
if (addr_cells == 1) {
|
||||
if ((addr > UINT32_MAX) || (((uint64_t) UINT32_MAX + 1 - addr) < size))
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
fdt32_st(prop, (uint32_t)addr);
|
||||
} else if (addr_cells == 2) {
|
||||
fdt64_st(prop, addr);
|
||||
} else {
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
}
|
||||
|
||||
/* check validity of size */
|
||||
prop += addr_cells * sizeof(fdt32_t);
|
||||
if (size_cells == 1) {
|
||||
if (size > UINT32_MAX)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
fdt32_st(prop, (uint32_t)size);
|
||||
} else if (size_cells == 2) {
|
||||
fdt64_st(prop, size);
|
||||
} else {
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
}
|
||||
|
||||
return fdt_appendprop(fdt, nodeoffset, name, data,
|
||||
(addr_cells + size_cells) * sizeof(fdt32_t));
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_check_full(const void *fdt, size_t bufsize)
|
||||
{
|
||||
int err;
|
||||
int num_memrsv;
|
||||
int offset, nextoffset = 0;
|
||||
uint32_t tag;
|
||||
unsigned int depth = 0;
|
||||
const void *prop;
|
||||
const char *propname;
|
||||
bool expect_end = false;
|
||||
|
||||
if (bufsize < FDT_V1_SIZE)
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
if (bufsize < fdt_header_size(fdt))
|
||||
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;
|
||||
|
||||
/* If we see two root nodes, something is wrong */
|
||||
if (expect_end && tag != FDT_END)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
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;
|
||||
|
||||
/* The root node must have an empty name */
|
||||
if (depth == 1) {
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
name = fdt_get_name(fdt, offset, &len);
|
||||
if (!name)
|
||||
return len;
|
||||
|
||||
if (*name || len)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
}
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
if (depth == 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
expect_end = true;
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
prop = fdt_getprop_by_offset(fdt, offset, &propname,
|
||||
&err);
|
||||
if (!prop)
|
||||
return err;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -FDT_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2012 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#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);
|
||||
}
|
||||
1101
libfdt/fdt_overlay.c
1101
libfdt/fdt_overlay.c
File diff suppressed because it is too large
Load diff
888
libfdt/fdt_ro.c
888
libfdt/fdt_ro.c
|
|
@ -1,888 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
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)
|
||||
{
|
||||
int32_t totalsize;
|
||||
uint32_t absoffset;
|
||||
size_t len;
|
||||
int err;
|
||||
const char *s, *n;
|
||||
|
||||
if (can_assume(VALID_INPUT)) {
|
||||
s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
|
||||
|
||||
if (lenp)
|
||||
*lenp = strlen(s);
|
||||
return s;
|
||||
}
|
||||
totalsize = fdt_ro_probe_(fdt);
|
||||
err = totalsize;
|
||||
if (totalsize < 0)
|
||||
goto fail;
|
||||
|
||||
err = -FDT_ERR_BADOFFSET;
|
||||
absoffset = stroffset + fdt_off_dt_strings(fdt);
|
||||
if (absoffset >= (unsigned)totalsize)
|
||||
goto fail;
|
||||
len = totalsize - absoffset;
|
||||
|
||||
if (fdt_magic(fdt) == FDT_MAGIC) {
|
||||
if (stroffset < 0)
|
||||
goto fail;
|
||||
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
|
||||
if ((unsigned)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) {
|
||||
unsigned int sw_stroffset = -stroffset;
|
||||
|
||||
if ((stroffset >= 0) ||
|
||||
(sw_stroffset > fdt_size_dt_strings(fdt)))
|
||||
goto fail;
|
||||
if (sw_stroffset < len)
|
||||
len = sw_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);
|
||||
}
|
||||
|
||||
int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
|
||||
{
|
||||
uint32_t max = 0;
|
||||
int offset = -1;
|
||||
|
||||
while (true) {
|
||||
uint32_t value;
|
||||
|
||||
offset = fdt_next_node(fdt, offset, NULL);
|
||||
if (offset < 0) {
|
||||
if (offset == -FDT_ERR_NOTFOUND)
|
||||
break;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
value = fdt_get_phandle(fdt, offset);
|
||||
|
||||
if (value > max)
|
||||
max = value;
|
||||
}
|
||||
|
||||
if (phandle)
|
||||
*phandle = max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
|
||||
{
|
||||
uint32_t max;
|
||||
int err;
|
||||
|
||||
err = fdt_find_max_phandle(fdt, &max);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (max == FDT_MAX_PHANDLE)
|
||||
return -FDT_ERR_NOPHANDLES;
|
||||
|
||||
if (phandle)
|
||||
*phandle = max + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
|
||||
{
|
||||
unsigned int offset = n * sizeof(struct fdt_reserve_entry);
|
||||
unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
|
||||
|
||||
if (!can_assume(VALID_INPUT)) {
|
||||
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 (!can_assume(VALID_INPUT) && !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);
|
||||
|
||||
if (!can_assume(VALID_INPUT) && namelen <= 0)
|
||||
return -FDT_ERR_BADPATH;
|
||||
|
||||
/* 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 (!can_assume(VALID_DTB) && (((err = fdt_ro_probe_(fdt)) < 0)
|
||||
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
|
||||
goto fail;
|
||||
|
||||
nameptr = nh->name;
|
||||
|
||||
if (!can_assume(LATEST) && 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 (!can_assume(VALID_INPUT) &&
|
||||
(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 (!can_assume(LATEST) && 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;
|
||||
|
||||
prop = fdt_get_property_by_offset_(fdt, offset, lenp);
|
||||
if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
|
||||
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 (!can_assume(LATEST) && 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 (!can_assume(LATEST) && 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;
|
||||
|
||||
if (!can_assume(VALID_INPUT)) {
|
||||
name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
|
||||
&namelen);
|
||||
*namep = name;
|
||||
if (!name) {
|
||||
if (lenp)
|
||||
*lenp = namelen;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
*namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle realignment */
|
||||
if (!can_assume(LATEST) && 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);
|
||||
}
|
||||
|
||||
static const void *fdt_path_getprop_namelen(const void *fdt, const char *path,
|
||||
const char *propname, int propnamelen,
|
||||
int *lenp)
|
||||
{
|
||||
int offset = fdt_path_offset(fdt, path);
|
||||
|
||||
if (offset < 0)
|
||||
return NULL;
|
||||
|
||||
return fdt_getprop_namelen(fdt, offset, propname, propnamelen, lenp);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias_namelen(const void *fdt,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int len;
|
||||
const char *alias;
|
||||
|
||||
alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len);
|
||||
|
||||
if (!can_assume(VALID_DTB) &&
|
||||
!(alias && len > 0 && alias[len - 1] == '\0' && *alias == '/'))
|
||||
return NULL;
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
const char *fdt_get_alias(const void *fdt, const char *name)
|
||||
{
|
||||
return fdt_get_alias_namelen(fdt, name, strlen(name));
|
||||
}
|
||||
|
||||
const char *fdt_get_symbol_namelen(const void *fdt,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
return fdt_path_getprop_namelen(fdt, "/__symbols__", name, namelen, NULL);
|
||||
}
|
||||
|
||||
const char *fdt_get_symbol(const void *fdt, const char *name)
|
||||
{
|
||||
return fdt_get_symbol_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 (!can_assume(VALID_INPUT)) {
|
||||
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 (can_assume(LIBFDT_FLAWLESS) || 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 == ~0U))
|
||||
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() */
|
||||
}
|
||||
507
libfdt/fdt_rw.c
507
libfdt/fdt_rw.c
|
|
@ -1,507 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
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)
|
||||
{
|
||||
if (can_assume(VALID_DTB))
|
||||
return 0;
|
||||
FDT_RO_PROBE(fdt);
|
||||
|
||||
if (!can_assume(LATEST) && 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 (!can_assume(LATEST) && 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 unsigned 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;
|
||||
unsigned int dsize = fdt_data_size_(fdt);
|
||||
size_t soff = p - (char *)fdt;
|
||||
|
||||
if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if (dsize - oldlen + newlen > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (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;
|
||||
}
|
||||
|
||||
/* Must only be used to roll back in case of error */
|
||||
static void fdt_del_last_string_(void *fdt, const char *s)
|
||||
{
|
||||
int newlen = strlen(s) + 1;
|
||||
|
||||
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_find_add_string_() - Find or allocate a string
|
||||
*
|
||||
* @fdt: pointer to the device tree to check/adjust
|
||||
* @s: string to find/add
|
||||
* @allocated: Set to 0 if the string was found, 1 if not found and so
|
||||
* allocated. Ignored if can_assume(NO_ROLLBACK)
|
||||
* @return offset of string in the string table (whether found or added)
|
||||
*/
|
||||
static int fdt_find_add_string_(void *fdt, const char *s, int slen,
|
||||
int *allocated)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
|
||||
const char *p;
|
||||
char *new;
|
||||
int err;
|
||||
|
||||
if (!can_assume(NO_ROLLBACK))
|
||||
*allocated = 0;
|
||||
|
||||
p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);
|
||||
if (p)
|
||||
/* found it */
|
||||
return (p - strtab);
|
||||
|
||||
new = strtab + fdt_size_dt_strings(fdt);
|
||||
err = fdt_splice_string_(fdt, slen + 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!can_assume(NO_ROLLBACK))
|
||||
*allocated = 1;
|
||||
|
||||
memcpy(new, s, slen);
|
||||
new[slen] = '\0';
|
||||
|
||||
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 namelen,
|
||||
int len, struct fdt_property **prop)
|
||||
{
|
||||
int oldlen;
|
||||
int err;
|
||||
|
||||
*prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,
|
||||
&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 namelen, int len, struct fdt_property **prop)
|
||||
{
|
||||
int proplen;
|
||||
int nextoffset;
|
||||
int namestroff;
|
||||
int err;
|
||||
int allocated;
|
||||
|
||||
if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
|
||||
return nextoffset;
|
||||
|
||||
namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
|
||||
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) {
|
||||
/* Delete the string if we failed to add it */
|
||||
if (!can_assume(NO_ROLLBACK) && allocated)
|
||||
fdt_del_last_string_(fdt, name);
|
||||
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_namelen(void *fdt, int nodeoffset, const char *name,
|
||||
int namelen, int len, void **prop_data)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int err;
|
||||
|
||||
FDT_RW_PROBE(fdt);
|
||||
|
||||
err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);
|
||||
if (err == -FDT_ERR_NOTFOUND)
|
||||
err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,
|
||||
&prop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*prop_data = prop->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
|
||||
int namelen, const void *val, int len)
|
||||
{
|
||||
void *prop_data;
|
||||
int err;
|
||||
|
||||
err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,
|
||||
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, strlen(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 */
|
||||
tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
|
||||
/* the fdt_subnode_offset_namelen() should ensure this never hits */
|
||||
if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
|
||||
return -FDT_ERR_INTERNAL;
|
||||
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 strings_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), strings_size);
|
||||
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 (can_assume(LATEST) || fdt_version(fdt) >= 17) {
|
||||
struct_size = fdt_size_dt_struct(fdt);
|
||||
} else if (fdt_version(fdt) == 16) {
|
||||
struct_size = 0;
|
||||
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
|
||||
;
|
||||
if (struct_size < 0)
|
||||
return struct_size;
|
||||
} else {
|
||||
return -FDT_ERR_BADVERSION;
|
||||
}
|
||||
|
||||
if (can_assume(LIBFDT_ORDER) ||
|
||||
!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,
|
||||
fdt_size_dt_strings(fdt));
|
||||
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_size_dt_strings(fdt));
|
||||
fdt_set_totalsize(fdt, fdt_data_size_(fdt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* 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),
|
||||
FDT_ERRTABENT(FDT_ERR_BADFLAGS),
|
||||
FDT_ERRTABENT(FDT_ERR_ALIGNMENT),
|
||||
};
|
||||
#define FDT_ERRTABSIZE ((int)(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>";
|
||||
}
|
||||
384
libfdt/fdt_sw.c
384
libfdt/fdt_sw.c
|
|
@ -1,384 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int fdt_sw_probe_(void *fdt)
|
||||
{
|
||||
if (!can_assume(VALID_INPUT)) {
|
||||
if (fdt_magic(fdt) == FDT_MAGIC)
|
||||
return -FDT_ERR_BADSTATE;
|
||||
else if (fdt_magic(fdt) != FDT_SW_MAGIC)
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
}
|
||||
|
||||
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_reservemap_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 (!can_assume(VALID_INPUT) && 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 (!can_assume(VALID_INPUT) &&
|
||||
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)
|
||||
{
|
||||
unsigned int offset = fdt_size_dt_struct(fdt);
|
||||
unsigned 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 int 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);
|
||||
|
||||
if (bufsize < 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
tailsize = fdt_size_dt_strings(fdt);
|
||||
|
||||
if (!can_assume(VALID_DTB) &&
|
||||
headsize + tailsize > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_INTERNAL;
|
||||
|
||||
if ((headsize + tailsize) > (unsigned)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);
|
||||
unsigned int strtabsize = fdt_size_dt_strings(fdt);
|
||||
unsigned int len = strlen(s) + 1;
|
||||
unsigned 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_LAST_COMPATIBLE_VERSION);
|
||||
fdt_set_magic(fdt, FDT_MAGIC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
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 ((unsigned)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;
|
||||
}
|
||||
2540
libfdt/libfdt.h
2540
libfdt/libfdt.h
File diff suppressed because it is too large
Load diff
|
|
@ -1,69 +0,0 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
|
||||
#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.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#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
|
||||
|
||||
#endif /* LIBFDT_ENV_H */
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
|
||||
#ifndef LIBFDT_INTERNAL_H
|
||||
#define LIBFDT_INTERNAL_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
#include <fdt.h>
|
||||
|
||||
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
|
||||
|
||||
int32_t fdt_ro_probe_(const void *fdt);
|
||||
#define FDT_RO_PROBE(fdt) \
|
||||
{ \
|
||||
if (!can_assume(VALID_DTB)) { \
|
||||
int32_t totalsize_; \
|
||||
if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
|
||||
return totalsize_; \
|
||||
} \
|
||||
}
|
||||
|
||||
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_len_(const char *strtab, int tabsize, const char *s,
|
||||
int s_len);
|
||||
static inline const char *fdt_find_string_(const char *strtab, int tabsize,
|
||||
const char *s)
|
||||
{
|
||||
return fdt_find_string_len_(strtab, tabsize, s, strlen(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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal helpers to access structural elements of the device tree
|
||||
* blob (rather than for example reading integers from within property
|
||||
* values). We assume that we are either given a naturally aligned
|
||||
* address for the platform or if we are not, we are on a platform
|
||||
* where unaligned memory reads will be handled in a graceful manner.
|
||||
* If not the external helpers fdtXX_ld() from libfdt.h can be used
|
||||
* instead.
|
||||
*/
|
||||
static inline uint32_t fdt32_ld_(const fdt32_t *p)
|
||||
{
|
||||
return fdt32_to_cpu(*p);
|
||||
}
|
||||
|
||||
static inline uint64_t fdt64_ld_(const fdt64_t *p)
|
||||
{
|
||||
return fdt64_to_cpu(*p);
|
||||
}
|
||||
|
||||
#define FDT_SW_MAGIC (~FDT_MAGIC)
|
||||
|
||||
/**********************************************************************/
|
||||
/* Checking controls */
|
||||
/**********************************************************************/
|
||||
|
||||
#ifndef FDT_ASSUME_MASK
|
||||
#define FDT_ASSUME_MASK 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Defines assumptions which can be enabled. Each of these can be enabled
|
||||
* individually. For maximum safety, don't enable any assumptions!
|
||||
*
|
||||
* For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
|
||||
* You should have another method of validating the device tree, such as a
|
||||
* signature or hash check before using libfdt.
|
||||
*
|
||||
* For situations where security is not a concern it may be safe to enable
|
||||
* ASSUME_PERFECT.
|
||||
*/
|
||||
enum {
|
||||
/*
|
||||
* This does essentially no checks. Only the latest device-tree
|
||||
* version is correctly handled. Inconsistencies or errors in the device
|
||||
* tree may cause undefined behaviour or crashes. Invalid parameters
|
||||
* passed to libfdt may do the same.
|
||||
*
|
||||
* If an error occurs when modifying the tree it may leave the tree in
|
||||
* an intermediate (but valid) state. As an example, adding a property
|
||||
* where there is insufficient space may result in the property name
|
||||
* being added to the string table even though the property itself is
|
||||
* not added to the struct section.
|
||||
*
|
||||
* Only use this if you have a fully validated device tree with
|
||||
* the latest supported version and wish to minimise code size.
|
||||
*/
|
||||
ASSUME_PERFECT = 0xff,
|
||||
|
||||
/*
|
||||
* This assumes that the device tree is sane. i.e. header metadata
|
||||
* and basic hierarchy are correct.
|
||||
*
|
||||
* With this assumption enabled, normal device trees produced by libfdt
|
||||
* and the compiler should be handled safely. Malicious device trees and
|
||||
* complete garbage may cause libfdt to behave badly or crash. Truncated
|
||||
* device trees (e.g. those only partially loaded) can also cause
|
||||
* problems.
|
||||
*
|
||||
* Note: Only checks that relate exclusively to the device tree itself
|
||||
* (not the parameters passed to libfdt) are disabled by this
|
||||
* assumption. This includes checking headers, tags and the like.
|
||||
*/
|
||||
ASSUME_VALID_DTB = 1 << 0,
|
||||
|
||||
/*
|
||||
* This builds on ASSUME_VALID_DTB and further assumes that libfdt
|
||||
* functions are called with valid parameters, i.e. not trigger
|
||||
* FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any
|
||||
* extensive checking of parameters and the device tree, making various
|
||||
* assumptions about correctness.
|
||||
*
|
||||
* It doesn't make sense to enable this assumption unless
|
||||
* ASSUME_VALID_DTB is also enabled.
|
||||
*/
|
||||
ASSUME_VALID_INPUT = 1 << 1,
|
||||
|
||||
/*
|
||||
* This disables checks for device-tree version and removes all code
|
||||
* which handles older versions.
|
||||
*
|
||||
* Only enable this if you know you have a device tree with the latest
|
||||
* version.
|
||||
*/
|
||||
ASSUME_LATEST = 1 << 2,
|
||||
|
||||
/*
|
||||
* This assumes that it is OK for a failed addition to the device tree,
|
||||
* due to lack of space or some other problem, to skip any rollback
|
||||
* steps (such as dropping the property name from the string table).
|
||||
* This is safe to enable in most circumstances, even though it may
|
||||
* leave the tree in a sub-optimal state.
|
||||
*/
|
||||
ASSUME_NO_ROLLBACK = 1 << 3,
|
||||
|
||||
/*
|
||||
* This assumes that the device tree components appear in a 'convenient'
|
||||
* order, i.e. the memory reservation block first, then the structure
|
||||
* block and finally the string block.
|
||||
*
|
||||
* This order is not specified by the device-tree specification,
|
||||
* but is expected by libfdt. The device-tree compiler always created
|
||||
* device trees with this order.
|
||||
*
|
||||
* This assumption disables a check in fdt_open_into() and removes the
|
||||
* ability to fix the problem there. This is safe if you know that the
|
||||
* device tree is correctly ordered. See fdt_blocks_misordered_().
|
||||
*/
|
||||
ASSUME_LIBFDT_ORDER = 1 << 4,
|
||||
|
||||
/*
|
||||
* This assumes that libfdt itself does not have any internal bugs. It
|
||||
* drops certain checks that should never be needed unless libfdt has an
|
||||
* undiscovered bug.
|
||||
*
|
||||
* This can generally be considered safe to enable.
|
||||
*/
|
||||
ASSUME_LIBFDT_FLAWLESS = 1 << 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* can_assume_() - check if a particular assumption is enabled
|
||||
*
|
||||
* @mask: Mask to check (ASSUME_...)
|
||||
* @return true if that assumption is enabled, else false
|
||||
*/
|
||||
static inline bool can_assume_(int mask)
|
||||
{
|
||||
return FDT_ASSUME_MASK & mask;
|
||||
}
|
||||
|
||||
/** helper macros for checking assumptions */
|
||||
#define can_assume(_assume) can_assume_(ASSUME_ ## _assume)
|
||||
|
||||
#endif /* LIBFDT_INTERNAL_H */
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
version_script = '-Wl,--version-script=@0@'.format(meson.current_source_dir() / 'version.lds')
|
||||
if not cc.has_link_argument(version_script)
|
||||
version_script = []
|
||||
endif
|
||||
|
||||
sources = files(
|
||||
'fdt.c',
|
||||
'fdt_addresses.c',
|
||||
'fdt_check.c',
|
||||
'fdt_empty_tree.c',
|
||||
'fdt_overlay.c',
|
||||
'fdt_ro.c',
|
||||
'fdt_rw.c',
|
||||
'fdt_strerror.c',
|
||||
'fdt_sw.c',
|
||||
'fdt_wip.c',
|
||||
)
|
||||
|
||||
link_args = []
|
||||
if cc.has_link_argument('-Wl,--no-undefined')
|
||||
link_args += '-Wl,--no-undefined'
|
||||
else
|
||||
# -undefined error is the equivalent of --no-undefined for the macOS linker,
|
||||
# but -undefined would also be understood as a valid argument for GNU ld!
|
||||
link_args += cc.get_supported_link_arguments('-Wl,-undefined,error')
|
||||
endif
|
||||
|
||||
link_args += version_script
|
||||
libfdt = library(
|
||||
'fdt', sources,
|
||||
version: meson.project_version(),
|
||||
link_args: link_args,
|
||||
link_depends: 'version.lds',
|
||||
install: get_option('default_library') != 'static' or not wheel_only,
|
||||
)
|
||||
|
||||
libfdt_inc = include_directories('.')
|
||||
|
||||
libfdt_dep = declare_dependency(
|
||||
include_directories: libfdt_inc,
|
||||
link_with: libfdt,
|
||||
)
|
||||
meson.override_dependency('libfdt', libfdt_dep)
|
||||
|
||||
if not wheel_only
|
||||
install_headers(
|
||||
files(
|
||||
'fdt.h',
|
||||
'libfdt.h',
|
||||
'libfdt_env.h',
|
||||
)
|
||||
)
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
|
||||
pkgconfig.generate(
|
||||
libraries: libfdt,
|
||||
version: meson.project_version(),
|
||||
filebase: 'libfdt',
|
||||
name: 'libfdt',
|
||||
description: 'Flat Device Tree manipulation',
|
||||
)
|
||||
endif
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"authors": [
|
||||
{
|
||||
"name": "@VCS_SBOM_AUTHORS@"
|
||||
}
|
||||
]
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:github/dgibson/libfdt@@VCS_TAG@",
|
||||
"cpe": "cpe:2.3:a:dgibson:libfdt:@VCS_TAG@:*:*:*:*:*:*:*",
|
||||
"name": "libfdt",
|
||||
"version": "@VCS_VERSION@",
|
||||
"description": "Utility library for reading and manipulating the FDT binary format",
|
||||
"supplier": {
|
||||
"name": "libfdt developers"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"license": {
|
||||
"id": "BSD-2-Clause"
|
||||
}
|
||||
},
|
||||
{
|
||||
"license": {
|
||||
"id": "GPL-2.0-or-later"
|
||||
}
|
||||
}
|
||||
],
|
||||
"externalReferences": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/dgibson/dtc"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
|
||||
LIBFDT_1.2 {
|
||||
global:
|
||||
fdt_next_node;
|
||||
fdt_check_header;
|
||||
fdt_move;
|
||||
fdt_string;
|
||||
fdt_num_mem_rsv;
|
||||
fdt_get_mem_rsv;
|
||||
fdt_subnode_offset_namelen;
|
||||
fdt_subnode_offset;
|
||||
fdt_path_offset_namelen;
|
||||
fdt_path_offset;
|
||||
fdt_get_name;
|
||||
fdt_get_property_namelen;
|
||||
fdt_get_property;
|
||||
fdt_getprop_namelen;
|
||||
fdt_getprop;
|
||||
fdt_get_phandle;
|
||||
fdt_get_alias_namelen;
|
||||
fdt_get_alias;
|
||||
fdt_get_path;
|
||||
fdt_header_size;
|
||||
fdt_supernode_atdepth_offset;
|
||||
fdt_node_depth;
|
||||
fdt_parent_offset;
|
||||
fdt_node_offset_by_prop_value;
|
||||
fdt_node_offset_by_phandle;
|
||||
fdt_node_check_compatible;
|
||||
fdt_node_offset_by_compatible;
|
||||
fdt_setprop_inplace;
|
||||
fdt_nop_property;
|
||||
fdt_nop_node;
|
||||
fdt_create;
|
||||
fdt_add_reservemap_entry;
|
||||
fdt_finish_reservemap;
|
||||
fdt_begin_node;
|
||||
fdt_property;
|
||||
fdt_end_node;
|
||||
fdt_finish;
|
||||
fdt_open_into;
|
||||
fdt_pack;
|
||||
fdt_add_mem_rsv;
|
||||
fdt_del_mem_rsv;
|
||||
fdt_set_name;
|
||||
fdt_setprop_namelen;
|
||||
fdt_setprop;
|
||||
fdt_delprop;
|
||||
fdt_add_subnode_namelen;
|
||||
fdt_add_subnode;
|
||||
fdt_del_node;
|
||||
fdt_strerror;
|
||||
fdt_offset_ptr;
|
||||
fdt_next_tag;
|
||||
fdt_appendprop;
|
||||
fdt_create_empty_tree;
|
||||
fdt_first_property_offset;
|
||||
fdt_get_property_by_offset;
|
||||
fdt_getprop_by_offset;
|
||||
fdt_next_property_offset;
|
||||
fdt_first_subnode;
|
||||
fdt_next_subnode;
|
||||
fdt_address_cells;
|
||||
fdt_size_cells;
|
||||
fdt_stringlist_contains;
|
||||
fdt_stringlist_count;
|
||||
fdt_stringlist_search;
|
||||
fdt_stringlist_get;
|
||||
fdt_resize;
|
||||
fdt_overlay_apply;
|
||||
fdt_get_string;
|
||||
fdt_find_max_phandle;
|
||||
fdt_generate_phandle;
|
||||
fdt_check_full;
|
||||
fdt_setprop_placeholder_namelen;
|
||||
fdt_setprop_placeholder;
|
||||
fdt_property_placeholder;
|
||||
fdt_header_size_;
|
||||
fdt_appendprop_addrrange;
|
||||
fdt_setprop_inplace_namelen_partial;
|
||||
fdt_create_with_flags;
|
||||
fdt_overlay_target_offset;
|
||||
fdt_get_symbol;
|
||||
fdt_get_symbol_namelen;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
1580
livetree.c
1580
livetree.c
File diff suppressed because it is too large
Load diff
132
meson.build
132
meson.build
|
|
@ -1,132 +0,0 @@
|
|||
project('dtc', 'c',
|
||||
version: files('VERSION.txt'),
|
||||
license: ['GPL2+', 'BSD-2'],
|
||||
default_options: ['werror=true', 'default_library=both'],
|
||||
meson_version: '>=0.57.0'
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
add_project_arguments(
|
||||
cc.get_supported_arguments([
|
||||
'-Wpointer-arith',
|
||||
'-Wcast-qual',
|
||||
'-Wnested-externs',
|
||||
'-Wstrict-prototypes',
|
||||
'-Wmissing-prototypes',
|
||||
'-Wredundant-decls',
|
||||
'-Wshadow',
|
||||
'-Wsuggest-attribute=format',
|
||||
'-Wwrite-strings',
|
||||
'-Wdiscarded-qualifiers',
|
||||
]),
|
||||
language: 'c'
|
||||
)
|
||||
|
||||
add_project_arguments(
|
||||
'-DFDT_ASSUME_MASK=' + get_option('assume-mask').to_string(),
|
||||
language: 'c'
|
||||
)
|
||||
|
||||
yamltree = 'yamltree.c'
|
||||
yaml = dependency('yaml-0.1', version: '>=0.2.3', required: get_option('yaml'))
|
||||
if not yaml.found()
|
||||
add_project_arguments('-DNO_YAML', language: 'c')
|
||||
yamltree = []
|
||||
endif
|
||||
|
||||
valgrind = dependency('valgrind', required: get_option('valgrind'))
|
||||
if not valgrind.found()
|
||||
add_project_arguments('-DNO_VALGRIND', language: 'c')
|
||||
endif
|
||||
|
||||
py = import('python')
|
||||
py = py.find_installation(required: get_option('python'))
|
||||
swig = find_program('swig', required: get_option('python'))
|
||||
pylibfdt_enabled = not meson.is_cross_build() and py.found() and swig.found() ? true : false
|
||||
wheel_only = get_option('wheel-only')
|
||||
|
||||
version_gen_h = vcs_tag(
|
||||
command: ['git', 'describe', '--dirty=+'],
|
||||
input: 'version_gen.h.in',
|
||||
output: 'version_gen.h',
|
||||
)
|
||||
|
||||
subdir('libfdt')
|
||||
|
||||
dtc_tools = []
|
||||
util_dep = declare_dependency(
|
||||
sources: ['util.c', version_gen_h],
|
||||
include_directories: '.',
|
||||
dependencies: libfdt_dep
|
||||
)
|
||||
|
||||
if get_option('tools') and not wheel_only
|
||||
flex = find_program('flex', required: true)
|
||||
bison = find_program('bison', required: true)
|
||||
|
||||
lgen = generator(
|
||||
flex,
|
||||
output: '@PLAINNAME@.lex.c',
|
||||
arguments: ['-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
|
||||
pgen = generator(
|
||||
bison,
|
||||
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
|
||||
arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'],
|
||||
)
|
||||
|
||||
if cc.check_header('fnmatch.h')
|
||||
dtc_tools += executable(
|
||||
'convert-dtsv0',
|
||||
[
|
||||
lgen.process('convert-dtsv0-lexer.l'),
|
||||
'srcpos.c',
|
||||
],
|
||||
dependencies: util_dep,
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
|
||||
dtc_tools += executable(
|
||||
'dtc',
|
||||
[
|
||||
lgen.process('dtc-lexer.l'),
|
||||
pgen.process('dtc-parser.y'),
|
||||
'checks.c',
|
||||
'data.c',
|
||||
'dtc.c',
|
||||
'flattree.c',
|
||||
'fstree.c',
|
||||
'livetree.c',
|
||||
'srcpos.c',
|
||||
'treesource.c',
|
||||
yamltree,
|
||||
],
|
||||
dependencies: [util_dep, yaml],
|
||||
install: true,
|
||||
)
|
||||
|
||||
foreach e: ['fdtdump', 'fdtget', 'fdtput', 'fdtoverlay']
|
||||
dtc_tools += executable(e, files(e + '.c'), dependencies: util_dep, install: true)
|
||||
endforeach
|
||||
|
||||
install_data(
|
||||
'dtdiff',
|
||||
install_dir: get_option('bindir'),
|
||||
install_mode: 'rwxr-xr-x',
|
||||
)
|
||||
endif
|
||||
|
||||
foreach e: dtc_tools
|
||||
meson.override_find_program(e.name(), e)
|
||||
endforeach
|
||||
|
||||
if pylibfdt_enabled
|
||||
subdir('pylibfdt')
|
||||
endif
|
||||
|
||||
if get_option('tests')
|
||||
subdir('tests')
|
||||
endif
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
option('tools', type: 'boolean', value: true,
|
||||
description: 'Build tools')
|
||||
option('assume-mask', type: 'integer', value: 0,
|
||||
description: 'Control the assumptions made (e.g. risking security issues) in the code.')
|
||||
option('yaml', type: 'feature', value: 'auto',
|
||||
description: 'YAML support')
|
||||
option('valgrind', type: 'feature', value: 'auto',
|
||||
description: 'Valgrind support')
|
||||
option('python', type: 'feature', value: 'auto',
|
||||
description: 'Build pylibfdt Python library')
|
||||
option('tests', type: 'boolean', value: true,
|
||||
description: 'Build tests')
|
||||
option('wheel-only', type: 'boolean', value: false,
|
||||
description: 'building from meson-python')
|
||||
3
pylibfdt/.gitignore
vendored
3
pylibfdt/.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
libfdt.py
|
||||
*.pyc
|
||||
libfdt_wrap.c
|
||||
1228
pylibfdt/libfdt.i
1228
pylibfdt/libfdt.i
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +0,0 @@
|
|||
libfdt_c = custom_target(
|
||||
'swig',
|
||||
input: 'libfdt.i',
|
||||
output: ['libfdt.c', 'libfdt.py'],
|
||||
install: true,
|
||||
install_dir: [false, py.get_install_dir(pure: false)],
|
||||
command: [swig, '-python', '-I'+meson.current_source_dir() / '../libfdt', '-o', '@OUTPUT0@', '@INPUT@']
|
||||
)
|
||||
|
||||
nowarn_gen = cc.get_supported_arguments(
|
||||
'-Wno-cast-qual',
|
||||
'-Wno-missing-prototypes',
|
||||
'-Wno-redundant-decls',
|
||||
)
|
||||
pylibfdt = py.extension_module(
|
||||
'_libfdt',
|
||||
libfdt_c,
|
||||
c_args: ['-DPY_SSIZE_T_CLEAN'] + nowarn_gen,
|
||||
dependencies: [libfdt_dep, py.dependency()],
|
||||
install: true,
|
||||
)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
[build-system]
|
||||
build-backend = 'mesonpy'
|
||||
requires = ['meson-python']
|
||||
|
||||
[project]
|
||||
name = 'libfdt'
|
||||
authors = [
|
||||
{name = 'Simon Glass', email = 'sjg@chromium.org'},
|
||||
]
|
||||
classifiers = [
|
||||
'Programming Language :: Python :: 3',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
|
||||
'Operating System :: OS Independent',
|
||||
]
|
||||
description = 'Python binding for libfdt'
|
||||
readme = 'README.md'
|
||||
requires-python = '>=3.8'
|
||||
dynamic = ['version']
|
||||
|
||||
[project.urls]
|
||||
'homepage' = 'https://git.kernel.org/pub/scm/utils/dtc/dtc.git'
|
||||
|
||||
# These arguments are applied only when building a redistributable binary wheel
|
||||
# for uploading to PyPI. We don't want to install libraries (or headers /
|
||||
# pkgconfig files / executables) that clash with system C installs, so we
|
||||
# disable everything other than the python bindings themselves, and build the
|
||||
# python C-API extension using static linkage to avoid juggling "libdir" /
|
||||
# LD_LIBRARY_PATH / RPATH around. When building both the C library and the
|
||||
# python bindings for a distro, `meson setup` will still default to shared
|
||||
# libraries.
|
||||
[tool.meson-python.args]
|
||||
setup = ['-Ddefault_library=static', '-Dwheel-only=true']
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
if [ -f /etc/os-release ]
|
||||
then
|
||||
. /etc/os-release
|
||||
else
|
||||
echo "ERROR: OS name is not provided."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$NAME" = "Arch Linux" ]
|
||||
then
|
||||
pacman -Syu --needed --noconfirm bison diffutils flex gcc git libyaml \
|
||||
make meson pkgconf python python-setuptools-scm swig
|
||||
elif [ "$NAME" = "Alpine Linux" ]
|
||||
then
|
||||
apk add build-base bison flex git yaml yaml-dev python3-dev \
|
||||
meson py3-setuptools_scm swig
|
||||
elif [ "$NAME" = "Fedora Linux" ]
|
||||
then
|
||||
dnf install -y bison diffutils flex gcc git libyaml libyaml-devel \
|
||||
make meson python3-devel python3-setuptools swig
|
||||
elif [ "$NAME" = "Ubuntu" ]
|
||||
then
|
||||
apt update
|
||||
apt install -yq build-essential bison flex git libyaml-dev pkg-config \
|
||||
meson python3-dev python3-setuptools python3-setuptools-scm swig
|
||||
else
|
||||
echo "ERROR: OS name is not provided."
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#! /bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
REMOTE_GIT=/pub/scm/utils/dtc/dtc.git
|
||||
REMOTE_PATH=/pub/software/utils/dtc
|
||||
|
||||
set -e
|
||||
|
||||
kup_one () {
|
||||
VERSION="$1"
|
||||
|
||||
TAG="v$VERSION"
|
||||
|
||||
PREFIX="dtc-$VERSION/"
|
||||
TAR="dtc-$VERSION.tar"
|
||||
SIG="$TAR.sign"
|
||||
|
||||
git archive --format=tar --prefix="$PREFIX" -o "$TAR" "$TAG"
|
||||
gpg --detach-sign --armor -o "$SIG" "$TAR"
|
||||
|
||||
ls -l "$TAR"*
|
||||
|
||||
# Verify the signature as a sanity check
|
||||
gpg --verify "$SIG" "$TAR"
|
||||
|
||||
kup put --tar --prefix="$PREFIX" "$REMOTE_GIT" "$TAG" "$SIG" "$REMOTE_PATH/$TAR.gz"
|
||||
}
|
||||
|
||||
for version; do
|
||||
kup_one $version
|
||||
done
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Print additional version information for non-release trees.
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [srctree]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cd "${1:-.}" || usage
|
||||
|
||||
# Check for git and a git repo.
|
||||
if head=`git rev-parse --verify HEAD 2>/dev/null`; then
|
||||
# Do we have an untagged version?
|
||||
if git name-rev --tags HEAD | grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then
|
||||
printf '%s%s' -g `echo "$head" | cut -c1-8`
|
||||
fi
|
||||
|
||||
# Are there uncommitted changes?
|
||||
if git diff-index HEAD | read dummy; then
|
||||
printf '%s' -dirty
|
||||
fi
|
||||
fi
|
||||
441
srcpos.c
441
srcpos.c
|
|
@ -1,441 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
/* A node in our list of directories to search for source/include files */
|
||||
struct search_path {
|
||||
struct search_path *next; /* next node in list, NULL for end */
|
||||
const char *dirname; /* name of directory to search */
|
||||
};
|
||||
|
||||
/* This is the list of directories that we search for source files */
|
||||
static struct search_path *search_path_head, **search_path_tail;
|
||||
|
||||
/* Detect infinite include recursion. */
|
||||
#define MAX_SRCFILE_DEPTH (200)
|
||||
static int srcfile_depth; /* = 0 */
|
||||
|
||||
static char *get_dirname(const char *path)
|
||||
{
|
||||
const char *slash = strrchr(path, '/');
|
||||
|
||||
if (slash) {
|
||||
int len = slash - path;
|
||||
char *dir = xmalloc(len + 1);
|
||||
|
||||
memcpy(dir, path, len);
|
||||
dir[len] = '\0';
|
||||
return dir;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *depfile; /* = NULL */
|
||||
struct srcfile_state *current_srcfile; /* = NULL */
|
||||
static char *initial_path; /* = NULL */
|
||||
static int initial_pathlen; /* = 0 */
|
||||
static bool initial_cpp = true;
|
||||
|
||||
static void set_initial_path(char *fname)
|
||||
{
|
||||
int i, len = strlen(fname);
|
||||
|
||||
xasprintf(&initial_path, "%s", fname);
|
||||
initial_pathlen = 0;
|
||||
for (i = 0; i != len; i++)
|
||||
if (initial_path[i] == '/')
|
||||
initial_pathlen++;
|
||||
}
|
||||
|
||||
static char *shorten_to_initial_path(char *fname)
|
||||
{
|
||||
char *p1, *p2, *prevslash1 = NULL;
|
||||
int slashes = 0;
|
||||
|
||||
for (p1 = fname, p2 = initial_path; *p1 && *p2; p1++, p2++) {
|
||||
if (*p1 != *p2)
|
||||
break;
|
||||
if (*p1 == '/') {
|
||||
prevslash1 = p1;
|
||||
slashes++;
|
||||
}
|
||||
}
|
||||
p1 = prevslash1 + 1;
|
||||
if (prevslash1) {
|
||||
int diff = initial_pathlen - slashes, i, j;
|
||||
int restlen = strlen(fname) - (p1 - fname);
|
||||
char *res;
|
||||
|
||||
res = xmalloc((3 * diff) + restlen + 1);
|
||||
for (i = 0, j = 0; i != diff; i++) {
|
||||
res[j++] = '.';
|
||||
res[j++] = '.';
|
||||
res[j++] = '/';
|
||||
}
|
||||
strcpy(res + j, p1);
|
||||
return res;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given path is an absolute one.
|
||||
*
|
||||
* On Windows, it either needs to begin with a forward slash or with a drive
|
||||
* letter (e.g. "C:").
|
||||
* On all other operating systems, it must begin with a forward slash to be
|
||||
* considered an absolute path.
|
||||
*/
|
||||
static bool is_absolute_path(const char *path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (
|
||||
path[0] == '/' ||
|
||||
(((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':')
|
||||
);
|
||||
#else
|
||||
return (path[0] == '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open a file in a given directory.
|
||||
*
|
||||
* If the filename is an absolute path, then dirname is ignored. If it is a
|
||||
* relative path, then we look in that directory for the file.
|
||||
*
|
||||
* @param dirname Directory to look in, or NULL for none
|
||||
* @param fname Filename to look for
|
||||
* @param fp Set to NULL if file did not open
|
||||
* @return allocated filename on success (caller must free), NULL on failure
|
||||
*/
|
||||
static char *try_open(const char *dirname, const char *fname, FILE **fp)
|
||||
{
|
||||
char *fullname;
|
||||
|
||||
if (!dirname || is_absolute_path(fname))
|
||||
fullname = xstrdup(fname);
|
||||
else
|
||||
fullname = join_path(dirname, fname);
|
||||
|
||||
*fp = fopen(fullname, "rb");
|
||||
if (!*fp) {
|
||||
free(fullname);
|
||||
fullname = NULL;
|
||||
}
|
||||
|
||||
return fullname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a file for read access
|
||||
*
|
||||
* If it is a relative filename, we search the full search path for it.
|
||||
*
|
||||
* @param fname Filename to open
|
||||
* @param fp Returns pointer to opened FILE, or NULL on failure
|
||||
* @return pointer to allocated filename, which caller must free
|
||||
*/
|
||||
static char *fopen_any_on_path(const char *fname, FILE **fp)
|
||||
{
|
||||
const char *cur_dir = NULL;
|
||||
struct search_path *node;
|
||||
char *fullname;
|
||||
|
||||
/* Try current directory first */
|
||||
assert(fp);
|
||||
if (current_srcfile)
|
||||
cur_dir = current_srcfile->dir;
|
||||
fullname = try_open(cur_dir, fname, fp);
|
||||
|
||||
/* Failing that, try each search path in turn */
|
||||
for (node = search_path_head; !*fp && node; node = node->next)
|
||||
fullname = try_open(node->dirname, fname, fp);
|
||||
|
||||
return fullname;
|
||||
}
|
||||
|
||||
FILE *srcfile_relative_open(const char *fname, char **fullnamep)
|
||||
{
|
||||
FILE *f;
|
||||
char *fullname;
|
||||
|
||||
if (streq(fname, "-")) {
|
||||
f = stdin;
|
||||
fullname = xstrdup("<stdin>");
|
||||
} else {
|
||||
fullname = fopen_any_on_path(fname, &f);
|
||||
if (!f)
|
||||
die("Couldn't open \"%s\": %s\n", fname,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (depfile) {
|
||||
fputc(' ', depfile);
|
||||
fprint_path_escaped(depfile, fullname);
|
||||
}
|
||||
|
||||
if (fullnamep)
|
||||
*fullnamep = fullname;
|
||||
else
|
||||
free(fullname);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void srcfile_push(const char *fname)
|
||||
{
|
||||
struct srcfile_state *srcfile;
|
||||
|
||||
if (srcfile_depth++ >= MAX_SRCFILE_DEPTH)
|
||||
die("Includes nested too deeply");
|
||||
|
||||
srcfile = xmalloc(sizeof(*srcfile));
|
||||
|
||||
srcfile->f = srcfile_relative_open(fname, &srcfile->name);
|
||||
srcfile->dir = get_dirname(srcfile->name);
|
||||
srcfile->prev = current_srcfile;
|
||||
|
||||
srcfile->lineno = 1;
|
||||
srcfile->colno = 1;
|
||||
|
||||
current_srcfile = srcfile;
|
||||
|
||||
if (srcfile_depth == 1)
|
||||
set_initial_path(srcfile->name);
|
||||
}
|
||||
|
||||
bool srcfile_pop(void)
|
||||
{
|
||||
struct srcfile_state *srcfile = current_srcfile;
|
||||
|
||||
assert(srcfile);
|
||||
|
||||
current_srcfile = srcfile->prev;
|
||||
|
||||
if (fclose(srcfile->f))
|
||||
die("Error closing \"%s\": %s\n", srcfile->name,
|
||||
strerror(errno));
|
||||
|
||||
/* FIXME: We allow the srcfile_state structure to leak,
|
||||
* because it could still be referenced from a location
|
||||
* variable being carried through the parser somewhere. To
|
||||
* fix this we could either allocate all the files from a
|
||||
* table, or use a pool allocator. */
|
||||
|
||||
return current_srcfile ? true : false;
|
||||
}
|
||||
|
||||
void srcfile_add_search_path(const char *dirname)
|
||||
{
|
||||
struct search_path *node;
|
||||
|
||||
/* Create the node */
|
||||
node = xmalloc(sizeof(*node));
|
||||
node->next = NULL;
|
||||
node->dirname = xstrdup(dirname);
|
||||
|
||||
/* Add to the end of our list */
|
||||
if (search_path_tail)
|
||||
*search_path_tail = node;
|
||||
else
|
||||
search_path_head = node;
|
||||
search_path_tail = &node->next;
|
||||
}
|
||||
|
||||
void srcpos_update(struct srcpos *pos, const char *text, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pos->file = current_srcfile;
|
||||
|
||||
pos->first_line = current_srcfile->lineno;
|
||||
pos->first_column = current_srcfile->colno;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (text[i] == '\n') {
|
||||
current_srcfile->lineno++;
|
||||
current_srcfile->colno = 1;
|
||||
} else {
|
||||
current_srcfile->colno++;
|
||||
}
|
||||
|
||||
pos->last_line = current_srcfile->lineno;
|
||||
pos->last_column = current_srcfile->colno;
|
||||
}
|
||||
|
||||
struct srcpos *
|
||||
srcpos_copy(struct srcpos *pos)
|
||||
{
|
||||
struct srcpos *pos_new;
|
||||
struct srcfile_state *srcfile_state;
|
||||
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
pos_new = xmalloc(sizeof(struct srcpos));
|
||||
assert(pos->next == NULL);
|
||||
memcpy(pos_new, pos, sizeof(struct srcpos));
|
||||
|
||||
/* allocate without free */
|
||||
srcfile_state = xmalloc(sizeof(struct srcfile_state));
|
||||
memcpy(srcfile_state, pos->file, sizeof(struct srcfile_state));
|
||||
pos_new->file = srcfile_state;
|
||||
|
||||
return pos_new;
|
||||
}
|
||||
|
||||
struct srcpos *srcpos_extend(struct srcpos *pos, struct srcpos *newtail)
|
||||
{
|
||||
struct srcpos *p;
|
||||
|
||||
if (!pos)
|
||||
return newtail;
|
||||
|
||||
for (p = pos; p->next != NULL; p = p->next);
|
||||
p->next = newtail;
|
||||
return pos;
|
||||
}
|
||||
|
||||
void srcpos_free(struct srcpos *pos)
|
||||
{
|
||||
struct srcpos *p_next;
|
||||
|
||||
while (pos) {
|
||||
p_next = pos->next;
|
||||
free(pos);
|
||||
pos = p_next;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
srcpos_string(struct srcpos *pos)
|
||||
{
|
||||
const char *fname = "<no-file>";
|
||||
char *pos_str;
|
||||
|
||||
if (pos->file && pos->file->name)
|
||||
fname = pos->file->name;
|
||||
|
||||
|
||||
if (pos->first_line != pos->last_line)
|
||||
xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_line, pos->last_column);
|
||||
else if (pos->first_column != pos->last_column)
|
||||
xasprintf(&pos_str, "%s:%d.%d-%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_column);
|
||||
else
|
||||
xasprintf(&pos_str, "%s:%d.%d", fname,
|
||||
pos->first_line, pos->first_column);
|
||||
|
||||
return pos_str;
|
||||
}
|
||||
|
||||
static char *
|
||||
srcpos_string_comment(struct srcpos *pos, bool first_line, int level)
|
||||
{
|
||||
char *pos_str, *fresh_fname = NULL, *first, *rest;
|
||||
const char *fname;
|
||||
|
||||
if (!pos) {
|
||||
if (level > 1) {
|
||||
xasprintf(&pos_str, "<no-file>:<no-line>");
|
||||
return pos_str;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pos->file)
|
||||
fname = "<no-file>";
|
||||
else if (!pos->file->name)
|
||||
fname = "<no-filename>";
|
||||
else if (level > 1)
|
||||
fname = pos->file->name;
|
||||
else {
|
||||
fresh_fname = shorten_to_initial_path(pos->file->name);
|
||||
if (fresh_fname)
|
||||
fname = fresh_fname;
|
||||
else
|
||||
fname = pos->file->name;
|
||||
}
|
||||
|
||||
if (level > 1)
|
||||
xasprintf(&first, "%s:%d:%d-%d:%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_line, pos->last_column);
|
||||
else
|
||||
xasprintf(&first, "%s:%d", fname,
|
||||
first_line ? pos->first_line : pos->last_line);
|
||||
|
||||
if (fresh_fname)
|
||||
free(fresh_fname);
|
||||
|
||||
if (pos->next != NULL) {
|
||||
rest = srcpos_string_comment(pos->next, first_line, level);
|
||||
xasprintf(&pos_str, "%s, %s", first, rest);
|
||||
free(first);
|
||||
free(rest);
|
||||
} else {
|
||||
pos_str = first;
|
||||
}
|
||||
|
||||
return pos_str;
|
||||
}
|
||||
|
||||
char *srcpos_string_first(struct srcpos *pos, int level)
|
||||
{
|
||||
return srcpos_string_comment(pos, true, level);
|
||||
}
|
||||
|
||||
char *srcpos_string_last(struct srcpos *pos, int level)
|
||||
{
|
||||
return srcpos_string_comment(pos, false, level);
|
||||
}
|
||||
|
||||
void srcpos_verror(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, va_list va)
|
||||
{
|
||||
char *srcstr;
|
||||
|
||||
srcstr = srcpos_string(pos);
|
||||
|
||||
fprintf(stderr, "%s: %s ", prefix, srcstr);
|
||||
vfprintf(stderr, fmt, va);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
free(srcstr);
|
||||
}
|
||||
|
||||
void srcpos_error(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
srcpos_verror(pos, prefix, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void srcpos_set_line(char *f, int l)
|
||||
{
|
||||
current_srcfile->name = f;
|
||||
current_srcfile->lineno = l;
|
||||
|
||||
if (initial_cpp) {
|
||||
initial_cpp = false;
|
||||
set_initial_path(f);
|
||||
}
|
||||
}
|
||||
104
srcpos.h
104
srcpos.h
|
|
@ -1,104 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#ifndef SRCPOS_H
|
||||
#define SRCPOS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
struct srcfile_state {
|
||||
FILE *f;
|
||||
char *name;
|
||||
char *dir;
|
||||
int lineno, colno;
|
||||
struct srcfile_state *prev;
|
||||
};
|
||||
|
||||
extern FILE *depfile; /* = NULL */
|
||||
extern struct srcfile_state *current_srcfile; /* = NULL */
|
||||
|
||||
/**
|
||||
* Open a source file.
|
||||
*
|
||||
* If the source file is a relative pathname, then it is searched for in the
|
||||
* current directory (the directory of the last source file read) and after
|
||||
* that in the search path.
|
||||
*
|
||||
* We work through the search path in order from the first path specified to
|
||||
* the last.
|
||||
*
|
||||
* If the file is not found, then this function does not return, but calls
|
||||
* die().
|
||||
*
|
||||
* @param fname Filename to search
|
||||
* @param fullnamep If non-NULL, it is set to the allocated filename of the
|
||||
* file that was opened. The caller is then responsible
|
||||
* for freeing the pointer.
|
||||
* @return pointer to opened FILE
|
||||
*/
|
||||
FILE *srcfile_relative_open(const char *fname, char **fullnamep);
|
||||
|
||||
void srcfile_push(const char *fname);
|
||||
bool srcfile_pop(void);
|
||||
|
||||
/**
|
||||
* Add a new directory to the search path for input files
|
||||
*
|
||||
* The new path is added at the end of the list.
|
||||
*
|
||||
* @param dirname Directory to add
|
||||
*/
|
||||
void srcfile_add_search_path(const char *dirname);
|
||||
|
||||
struct srcpos {
|
||||
int first_line;
|
||||
int first_column;
|
||||
int last_line;
|
||||
int last_column;
|
||||
struct srcfile_state *file;
|
||||
struct srcpos *next;
|
||||
};
|
||||
|
||||
#define YYLTYPE struct srcpos
|
||||
|
||||
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
|
||||
(Current).first_column = YYRHSLOC(Rhs, 1).first_column; \
|
||||
(Current).last_line = YYRHSLOC(Rhs, N).last_line; \
|
||||
(Current).last_column = YYRHSLOC (Rhs, N).last_column; \
|
||||
(Current).file = YYRHSLOC(Rhs, N).file; \
|
||||
} else { \
|
||||
(Current).first_line = (Current).last_line = \
|
||||
YYRHSLOC(Rhs, 0).last_line; \
|
||||
(Current).first_column = (Current).last_column = \
|
||||
YYRHSLOC(Rhs, 0).last_column; \
|
||||
(Current).file = YYRHSLOC (Rhs, 0).file; \
|
||||
} \
|
||||
(Current).next = NULL; \
|
||||
} while (0)
|
||||
|
||||
|
||||
extern void srcpos_update(struct srcpos *pos, const char *text, int len);
|
||||
extern struct srcpos *srcpos_copy(struct srcpos *pos);
|
||||
extern struct srcpos *srcpos_extend(struct srcpos *new_srcpos,
|
||||
struct srcpos *old_srcpos);
|
||||
extern void srcpos_free(struct srcpos *pos);
|
||||
extern char *srcpos_string(struct srcpos *pos);
|
||||
extern char *srcpos_string_first(struct srcpos *pos, int level);
|
||||
extern char *srcpos_string_last(struct srcpos *pos, int level);
|
||||
|
||||
|
||||
extern void PRINTF(3, 0) srcpos_verror(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, va_list va);
|
||||
extern void PRINTF(3, 4) srcpos_error(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, ...);
|
||||
|
||||
extern void srcpos_set_line(char *f, int l);
|
||||
|
||||
#endif /* SRCPOS_H */
|
||||
54
test.dts
Normal file
54
test.dts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/memreserve/ 1000000000000000 0000000002000000;
|
||||
/memreserve/ 2000000000000000-20ffffffffffffff;
|
||||
/memreserve/ 0-13;
|
||||
|
||||
/ {
|
||||
model = "MyBoardName";
|
||||
compatible = "MyBoardFamilyName";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
cpus {
|
||||
linux,phandle = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
PowerPC,970@0 {
|
||||
name = "PowerPC,970";
|
||||
device_type = "cpu";
|
||||
reg = <0>;
|
||||
clock-frequency = <5f5e1000>;
|
||||
timebase-frequency = <1FCA055>;
|
||||
linux,boot-cpu;
|
||||
i-cache-size = <10000>;
|
||||
d-cache-size = <8000>;
|
||||
};
|
||||
|
||||
PowerPC,970@1 {
|
||||
name = "PowerPC,970";
|
||||
device_type = "cpu";
|
||||
reg = <1>;
|
||||
clock-frequency = <5f5e1000>;
|
||||
timebase-frequency = <1FCA055>;
|
||||
i-cache-size = <10000>;
|
||||
d-cache-size = <8000>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
randomnode {
|
||||
string = "\xff\0stuffstuff\t\t\t\n\n\n";
|
||||
blob = [0a 0b 0c 0d de ea ad be ef];
|
||||
ref = < &/memory@0 >;
|
||||
};
|
||||
|
||||
memory@0 {
|
||||
device_type = "memory";
|
||||
memreg: reg = <00000000 00000000 00000000 20000000>;
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "root=/dev/sda2";
|
||||
linux,platform = <00000600>;
|
||||
};
|
||||
|
||||
};
|
||||
77
tests/.gitignore
vendored
77
tests/.gitignore
vendored
|
|
@ -1,77 +0,0 @@
|
|||
*.dtb
|
||||
*.dts.test.s
|
||||
*.test.dts
|
||||
*.test.dt.yaml
|
||||
tmp.*
|
||||
/fs/
|
||||
/add_subnode_with_nops
|
||||
/addr_size_cells
|
||||
/addr_size_cells2
|
||||
/appendprop[12]
|
||||
/appendprop_addrrange
|
||||
/asm_tree_dump
|
||||
/boot-cpuid
|
||||
/char_literal
|
||||
/check_full
|
||||
/check_header
|
||||
/check_path
|
||||
/del_node
|
||||
/del_property
|
||||
/dtbs_equal_ordered
|
||||
/dtbs_equal_unordered
|
||||
/dtb_reverse
|
||||
/dumptrees
|
||||
/extra-terminating-null
|
||||
/find_property
|
||||
/get_alias
|
||||
/get_mem_rsv
|
||||
/get_name
|
||||
/get_path
|
||||
/get_phandle
|
||||
/getprop
|
||||
/get_prop_offset
|
||||
/incbin
|
||||
/integer-expressions
|
||||
/fs_tree1
|
||||
/mangle-layout
|
||||
/move_and_save
|
||||
/node_check_compatible
|
||||
/node_offset_by_compatible
|
||||
/node_offset_by_phandle
|
||||
/node_offset_by_prop_value
|
||||
/nop_node
|
||||
/nop_property
|
||||
/nopulate
|
||||
/notfound
|
||||
/open_pack
|
||||
/overlay
|
||||
/overlay_bad_fixup
|
||||
/parent_offset
|
||||
/path-references
|
||||
/path_offset
|
||||
/path_offset_aliases
|
||||
/phandle_format
|
||||
/property_iterate
|
||||
/propname_escapes
|
||||
/references
|
||||
/relref_merge
|
||||
/root_node
|
||||
/rw_tree1
|
||||
/rw_oom
|
||||
/set_name
|
||||
/setprop
|
||||
/setprop_inplace
|
||||
/sized_cells
|
||||
/string_escapes
|
||||
/stringlist
|
||||
/subnode_iterate
|
||||
/subnode_offset
|
||||
/supernode_atdepth_offset
|
||||
/sw_tree1
|
||||
/sw_states
|
||||
/truncated_property
|
||||
/truncated_string
|
||||
/truncated_memrsv
|
||||
/utilfdt_test
|
||||
/value-labels
|
||||
/get_next_tag_invalid_prop_len
|
||||
12
tests/Makefile
Normal file
12
tests/Makefile
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
DTC = ../dtc
|
||||
VG_DTC = valgrind --tool=memcheck ../dtc
|
||||
|
||||
check: all
|
||||
./run_all_tests.sh
|
||||
|
||||
all:
|
||||
|
||||
clean:
|
||||
rm -f *~
|
||||
|
||||
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
LIB_TESTS_L = get_mem_rsv \
|
||||
root_node find_property subnode_offset path_offset \
|
||||
get_name getprop get_prop_offset get_phandle \
|
||||
get_path supernode_atdepth_offset parent_offset \
|
||||
node_offset_by_prop_value node_offset_by_phandle \
|
||||
node_check_compatible node_offset_by_compatible \
|
||||
get_alias get_next_tag_invalid_prop_len \
|
||||
char_literal \
|
||||
sized_cells \
|
||||
notfound \
|
||||
addr_size_cells \
|
||||
addr_size_cells2 \
|
||||
appendprop_addrrange \
|
||||
stringlist \
|
||||
setprop_inplace nop_property nop_node \
|
||||
sw_tree1 sw_states \
|
||||
move_and_save mangle-layout nopulate \
|
||||
open_pack rw_tree1 rw_oom set_name setprop del_property del_node \
|
||||
appendprop1 appendprop2 propname_escapes \
|
||||
string_escapes references path-references phandle_format \
|
||||
boot-cpuid incbin relref_merge \
|
||||
extra-terminating-null \
|
||||
dtbs_equal_ordered \
|
||||
dtb_reverse dtbs_equal_unordered \
|
||||
add_subnode_with_nops path_offset_aliases \
|
||||
utilfdt_test \
|
||||
integer-expressions \
|
||||
property_iterate \
|
||||
subnode_iterate \
|
||||
overlay overlay_bad_fixup \
|
||||
check_path check_header check_full \
|
||||
fs_tree1
|
||||
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv
|
||||
|
||||
LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
ifneq ($(STATIC_BUILD),1)
|
||||
DL_LIB_TESTS_L = asm_tree_dump value-labels
|
||||
DL_LIB_TESTS = $(DL_LIB_TESTS_L:%=$(TESTS_PREFIX)%)
|
||||
endif
|
||||
|
||||
TESTS = $(LIB_TESTS) $(LIBTREE_TESTS) $(DL_LIB_TESTS)
|
||||
|
||||
TESTS_TREES_L = test_tree1.dtb bad_node_char.dtb bad_node_format.dtb \
|
||||
bad_prop_char.dtb ovf_size_strings.dtb truncated_property.dtb \
|
||||
truncated_string.dtb truncated_memrsv.dtb two_roots.dtb named_root.dtb
|
||||
TESTS_TREES = $(TESTS_TREES_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
TESTS_TARGETS = $(TESTS) $(TESTS_TREES)
|
||||
|
||||
TESTS_DEPFILES = $(TESTS:%=%.d) \
|
||||
$(addprefix $(TESTS_PREFIX),testutils.d trees.d dumptrees.d)
|
||||
|
||||
TESTS_CLEANFILES_L = $(STD_CLEANFILES) \
|
||||
*.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \
|
||||
dumptrees
|
||||
TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
|
||||
TESTS_CLEANDIRS_L = fs
|
||||
TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%)
|
||||
|
||||
.PHONY: tests
|
||||
tests: $(TESTS) $(TESTS_TREES)
|
||||
|
||||
$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep)
|
||||
|
||||
# Not necessary on all platforms; allow -ldl to be excluded instead of forcing
|
||||
# other platforms to patch it out.
|
||||
LIBDL = -ldl
|
||||
$(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep)
|
||||
@$(VECHO) LD [libdl] $@
|
||||
$(LINK.c) -o $@ $^ $(LIBDL)
|
||||
|
||||
$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o \
|
||||
util.o $(LIBFDT_dep)
|
||||
|
||||
$(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o
|
||||
|
||||
$(TESTS_TREES): $(TESTS_PREFIX)dumptrees
|
||||
@$(VECHO) DUMPTREES
|
||||
cd $(TESTS_PREFIX); ./dumptrees . >/dev/null
|
||||
|
||||
tests_clean:
|
||||
@$(VECHO) CLEAN "(tests)"
|
||||
rm -f $(TESTS_CLEANFILES)
|
||||
rm -rf $(TESTS_CLEANDIRS)
|
||||
|
||||
check: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh
|
||||
|
||||
ifeq ($(NO_VALGRIND),1)
|
||||
checkm:
|
||||
@echo "make checkm requires valgrind, but NO_VALGRIND=1"
|
||||
else
|
||||
checkm: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh -m
|
||||
endif
|
||||
|
||||
checkv: tests ${TESTS_BIN} $(TESTS_PYLIBFDT)
|
||||
cd $(TESTS_PREFIX); ./run_tests.sh -v
|
||||
|
||||
ifneq ($(DEPTARGETS),)
|
||||
-include $(TESTS_DEPFILES)
|
||||
endif
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for fdt_nop_node()
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
#define SPACE 65536
|
||||
|
||||
#define CHECK(code) \
|
||||
{ \
|
||||
err = (code); \
|
||||
if (err) \
|
||||
FAIL(#code ": %s", fdt_strerror(err)); \
|
||||
}
|
||||
|
||||
#define OFF_CHECK(off, code) \
|
||||
{ \
|
||||
(off) = (code); \
|
||||
if (off < 0) \
|
||||
FAIL(#code ": %s", fdt_strerror(off)); \
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt;
|
||||
int err;
|
||||
int offset;
|
||||
|
||||
test_init(argc, argv);
|
||||
|
||||
fdt = xmalloc(SPACE);
|
||||
|
||||
CHECK(fdt_create(fdt, SPACE));
|
||||
|
||||
CHECK(fdt_finish_reservemap(fdt));
|
||||
CHECK(fdt_begin_node(fdt, ""));
|
||||
CHECK(fdt_property_cell(fdt, "prop1", TEST_VALUE_1));
|
||||
CHECK(fdt_property_cell(fdt, "prop2", TEST_VALUE_2));
|
||||
CHECK(fdt_end_node(fdt));
|
||||
CHECK(fdt_finish(fdt));
|
||||
|
||||
verbose_printf("Built empty tree, totalsize = %d\n",
|
||||
fdt_totalsize(fdt));
|
||||
|
||||
CHECK(fdt_open_into(fdt, fdt, SPACE));
|
||||
|
||||
check_getprop_cell(fdt, 0, "prop1", TEST_VALUE_1);
|
||||
check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
|
||||
|
||||
CHECK(fdt_nop_property(fdt, 0, "prop1"));
|
||||
|
||||
check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
|
||||
|
||||
OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode"));
|
||||
|
||||
check_getprop_cell(fdt, 0, "prop2", TEST_VALUE_2);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for #address-cells and #size-cells handling
|
||||
* Copyright (C) 2014 David Gibson, <david@gibson.dropbear.id.au>
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
static void check_node(const void *fdt, const char *path, int ac, int sc)
|
||||
{
|
||||
int offset;
|
||||
int xac, xsc;
|
||||
|
||||
offset = fdt_path_offset(fdt, path);
|
||||
if (offset < 0)
|
||||
FAIL("Couldn't find path %s", path);
|
||||
|
||||
xac = fdt_address_cells(fdt, offset);
|
||||
xsc = fdt_size_cells(fdt, offset);
|
||||
|
||||
if (xac != ac)
|
||||
FAIL("Address cells for %s is %d instead of %d\n",
|
||||
path, xac, ac);
|
||||
if (xsc != sc)
|
||||
FAIL("Size cells for %s is %d instead of %d\n",
|
||||
path, xsc, sc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt;
|
||||
|
||||
if (argc != 2)
|
||||
CONFIG("Usage: %s <dtb file>\n", argv[0]);
|
||||
|
||||
test_init(argc, argv);
|
||||
fdt = load_blob(argv[1]);
|
||||
|
||||
check_node(fdt, "/", 2, 2);
|
||||
check_node(fdt, "/identity-bus@0", 2, 1);
|
||||
check_node(fdt, "/simple-bus@1000000", 2, 1);
|
||||
check_node(fdt, "/discrete-bus@2000000", 1, 0);
|
||||
check_node(fdt, "/c0", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
|
||||
check_node(fdt, "/c1", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
|
||||
check_node(fdt, "/c2", -FDT_ERR_BADNCELLS, -FDT_ERR_BADNCELLS);
|
||||
check_node(fdt, "/c3", -FDT_ERR_BADNCELLS, 0);
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for #address-cells and #size-cells handling
|
||||
* Copyright (C) 2014 David Gibson, <david@gibson.dropbear.id.au>
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
static void check_node(const void *fdt, const char *path, int ac, int sc)
|
||||
{
|
||||
int offset;
|
||||
int xac, xsc;
|
||||
|
||||
offset = fdt_path_offset(fdt, path);
|
||||
if (offset < 0)
|
||||
FAIL("Couldn't find path %s", path);
|
||||
|
||||
xac = fdt_address_cells(fdt, offset);
|
||||
xsc = fdt_size_cells(fdt, offset);
|
||||
|
||||
if (xac != ac)
|
||||
FAIL("Address cells for %s is %d instead of %d\n",
|
||||
path, xac, ac);
|
||||
if (xsc != sc)
|
||||
FAIL("Size cells for %s is %d instead of %d\n",
|
||||
path, xsc, sc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt;
|
||||
|
||||
if (argc != 2)
|
||||
CONFIG("Usage: %s <dtb file>\n", argv[0]);
|
||||
|
||||
test_init(argc, argv);
|
||||
fdt = load_blob(argv[1]);
|
||||
|
||||
check_node(fdt, "/", 2, 1);
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
compatible = "test_addresses";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
identity-bus@0 {
|
||||
};
|
||||
|
||||
simple-bus@1000000 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
|
||||
discrete-bus@2000000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
c0@0 {
|
||||
#address-cells = <1 1>;
|
||||
#size-cells = <1 1>;
|
||||
};
|
||||
|
||||
c1@0 {
|
||||
#address-cells = <0x80000000>;
|
||||
#size-cells = <0x80000000>;
|
||||
};
|
||||
|
||||
c2@0 {
|
||||
#address-cells = <5>;
|
||||
#size-cells = <5>;
|
||||
};
|
||||
|
||||
c3@0 {
|
||||
#address-cells = <0>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
aliases {
|
||||
empty = "";
|
||||
loop = "loop";
|
||||
nonull = [626164];
|
||||
relative = "s1/subsubnode";
|
||||
s1 = &sub1;
|
||||
ss1 = &subsub1;
|
||||
sss1 = &subsubsub1;
|
||||
};
|
||||
|
||||
sub1: subnode@1 {
|
||||
compatible = "subnode1";
|
||||
reg = <1>;
|
||||
|
||||
subsub1: subsubnode {
|
||||
compatible = "subsubnode1", "subsubnode";
|
||||
|
||||
subsubsub1: subsubsubnode {
|
||||
compatible = "subsubsubnode1", "subsubsubnode";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
prop-str = "hello world", "nastystring: \a\b\t\n\v\f\r\\\"";
|
||||
prop-int64 = /bits/ 64 <0xdeadbeef01abcdef 0xdeadbeef01abcdef>;
|
||||
prop-int = <0xdeadbeef 123456789>;
|
||||
prop-bytes = [00010203040001020304];
|
||||
};
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for fdt_appendprop()
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
#define SPACE 65536
|
||||
|
||||
#define CHECK(code) \
|
||||
{ \
|
||||
err = (code); \
|
||||
if (err) \
|
||||
FAIL(#code ": %s", fdt_strerror(err)); \
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt;
|
||||
int err;
|
||||
uint8_t bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
test_init(argc, argv);
|
||||
|
||||
/* Create an empty tree first */
|
||||
fdt = xmalloc(SPACE);
|
||||
CHECK(fdt_create(fdt, SPACE));
|
||||
CHECK(fdt_finish_reservemap(fdt));
|
||||
CHECK(fdt_begin_node(fdt, ""));
|
||||
CHECK(fdt_end_node(fdt));
|
||||
CHECK(fdt_finish(fdt));
|
||||
|
||||
/* Now use appendprop to add properties */
|
||||
CHECK(fdt_open_into(fdt, fdt, SPACE));
|
||||
|
||||
CHECK(fdt_appendprop(fdt, 0, "prop-bytes", bytes, sizeof(bytes)));
|
||||
CHECK(fdt_appendprop_cell(fdt, 0, "prop-int", TEST_VALUE_1));
|
||||
CHECK(fdt_appendprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
|
||||
CHECK(fdt_appendprop_string(fdt, 0, "prop-str", TEST_STRING_1));
|
||||
|
||||
CHECK(fdt_pack(fdt));
|
||||
|
||||
save_blob("appendprop1.test.dtb", fdt);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for fdt_appendprop()
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
#define SPACE 65536
|
||||
|
||||
#define CHECK(code) \
|
||||
{ \
|
||||
err = (code); \
|
||||
if (err) \
|
||||
FAIL(#code ": %s", fdt_strerror(err)); \
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt, *buf;
|
||||
int err;
|
||||
uint8_t bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
test_init(argc, argv);
|
||||
fdt = load_blob_arg(argc, argv);
|
||||
|
||||
buf = xmalloc(SPACE);
|
||||
CHECK(fdt_open_into(fdt, buf, SPACE));
|
||||
free(fdt);
|
||||
fdt = buf;
|
||||
|
||||
CHECK(fdt_appendprop(fdt, 0, "prop-bytes", bytes, sizeof(bytes)));
|
||||
CHECK(fdt_appendprop_cell(fdt, 0, "prop-int", TEST_VALUE_2));
|
||||
CHECK(fdt_appendprop_u64(fdt, 0, "prop-int64", TEST_VALUE64_1));
|
||||
CHECK(fdt_appendprop_string(fdt, 0, "prop-str", TEST_STRING_2));
|
||||
|
||||
CHECK(fdt_pack(fdt));
|
||||
|
||||
save_blob("appendprop2.test.dtb", fdt);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Testcase for fdt_appendprop_addrrange()
|
||||
* Copyright (C) 2018 AKASHI Takahiro, Linaro Limited
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *fdt, *buf;
|
||||
int offset, xac, xsc, num, i, err;
|
||||
uint64_t addr, size;
|
||||
|
||||
if (argc != 5)
|
||||
CONFIG("Usage: %s <dtb file> <address-cells> <size-cells> <num>\n",
|
||||
argv[0]);
|
||||
|
||||
test_init(argc, argv);
|
||||
fdt = load_blob(argv[1]);
|
||||
xac = strtol(argv[2], NULL, 10);
|
||||
xsc = strtol(argv[3], NULL, 10);
|
||||
num = strtol(argv[4], NULL, 10);
|
||||
|
||||
buf = xmalloc(0x1000);
|
||||
if (!buf)
|
||||
FAIL("Couldn't allocate temporary buffer");
|
||||
err = fdt_open_into(fdt, buf, 0x1000);
|
||||
if (err)
|
||||
FAIL("fdt_open_into(): %s", fdt_strerror(err));
|
||||
free(fdt);
|
||||
fdt = buf;
|
||||
|
||||
/* Set up */
|
||||
err = fdt_setprop_cell(fdt, 0, "#address-cells", xac);
|
||||
if (err)
|
||||
FAIL("fdt_setprop_cell(\"#address-cells\"): %s",
|
||||
fdt_strerror(err));
|
||||
err = fdt_setprop_cell(fdt, 0, "#size-cells", xsc);
|
||||
if (err)
|
||||
FAIL("fdt_setprop_cell(\"#size-cells\"): %s",
|
||||
fdt_strerror(err));
|
||||
|
||||
offset = fdt_path_offset(fdt, "/node@1");
|
||||
if (offset < 0)
|
||||
FAIL("Couldn't find path %s", "/node@1");
|
||||
|
||||
addr = TEST_MEMREGION_ADDR;
|
||||
if (xac > 1)
|
||||
addr += TEST_MEMREGION_ADDR_HI;
|
||||
size = TEST_MEMREGION_SIZE;
|
||||
if (xsc > 1)
|
||||
size += TEST_MEMREGION_SIZE_HI;
|
||||
|
||||
/*
|
||||
* Do test
|
||||
*/
|
||||
/* 1. repeat append's */
|
||||
for (i = 0; i < num; i++) {
|
||||
err = fdt_appendprop_addrrange(fdt, 0, offset,
|
||||
"prop-memregion", addr, size);
|
||||
if (err)
|
||||
FAIL("Failed to append[%d] \"prop-memregion\": %s",
|
||||
i, fdt_strerror(err));
|
||||
|
||||
check_getprop_addrrange(fdt, 0, offset, "prop-memregion",
|
||||
i + 1);
|
||||
|
||||
addr += size;
|
||||
size += TEST_MEMREGION_SIZE_INC;
|
||||
}
|
||||
|
||||
/* 2. default property name */
|
||||
addr = TEST_MEMREGION_ADDR;
|
||||
if (xac > 1)
|
||||
addr += TEST_MEMREGION_ADDR_HI;
|
||||
size = TEST_MEMREGION_SIZE;
|
||||
if (xsc > 1)
|
||||
size += TEST_MEMREGION_SIZE_HI;
|
||||
|
||||
err = fdt_appendprop_addrrange(fdt, 0, offset, "reg", addr, size);
|
||||
if (err)
|
||||
FAIL("Failed to set \"reg\": %s", fdt_strerror(err));
|
||||
check_getprop_addrrange(fdt, 0, offset, "reg", 1);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Tests if an asm tree built into a shared object matches a given dtb
|
||||
* Copyright (C) 2008 David Gibson, IBM Corporation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "testdata.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *sohandle;
|
||||
void *fdt;
|
||||
int err;
|
||||
|
||||
test_init(argc, argv);
|
||||
if (argc != 3)
|
||||
CONFIG("Usage: %s <so file> <dtb file>", argv[0]);
|
||||
|
||||
sohandle = dlopen(argv[1], RTLD_NOW);
|
||||
if (!sohandle)
|
||||
FAIL("Couldn't dlopen() %s", argv[1]);
|
||||
|
||||
fdt = dlsym(sohandle, "dt_blob_start");
|
||||
if (!fdt)
|
||||
FAIL("Couldn't locate \"dt_blob_start\" symbol in %s",
|
||||
argv[1]);
|
||||
|
||||
err = fdt_check_header(fdt);
|
||||
if (err != 0)
|
||||
FAIL("%s contains invalid tree: %s", argv[1],
|
||||
fdt_strerror(err));
|
||||
|
||||
save_blob(argv[2], fdt);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
node2 {
|
||||
chosen {
|
||||
bootargs = <0xdeadbeef>;
|
||||
stdout-path = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
node {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
dma-ranges = <0 0 0 0 0>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
node {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
gpio: gpio-controller {
|
||||
#gpio-cells = <3>;
|
||||
};
|
||||
|
||||
node {
|
||||
nr-gpios = <1>;
|
||||
foo-gpios = <&gpio>;
|
||||
bar-gpio = <&gpio 1 2 3>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/dts-v1/;
|
||||
/ {
|
||||
bar: bar {
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
bar_con: endpoint {
|
||||
remote-endpoint = <&foo_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
foo {
|
||||
port {
|
||||
foo_con: endpoint {
|
||||
remote-endpoint = <&bar_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/dts-v1/;
|
||||
/ {
|
||||
bar: bar {
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>; // should always be 0
|
||||
port@1 {
|
||||
reg = <1 2>; // should always contain only a single cell
|
||||
bar_con: endpoint {
|
||||
remote-endpoint = <&foo_con>;
|
||||
};
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
bar_con2: endpoint {
|
||||
remote-endpoint = <&foo_con2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
foo {
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>; // should always be 0
|
||||
foo_con: endpoint@1 {
|
||||
reg = <1 2>; // should always contain only a single cell
|
||||
remote-endpoint = <&bar_con>;
|
||||
};
|
||||
foo_con2: endpoint@2 {
|
||||
reg = <2>;
|
||||
remote-endpoint = <&bar_con2>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
foo {
|
||||
remote-endpoint = <0xdeadbeef>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
/dts-v1/;
|
||||
/ { endpoint {}; };
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
bar: bar {
|
||||
port {
|
||||
bar_con: endpoint {
|
||||
remote-endpoint = <&foo_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
foo_con: endpoint {
|
||||
remote-endpoint = <&bar_con>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
|
||||
bar: bar {
|
||||
port {
|
||||
bar_con: endpoint {
|
||||
remote-endpoint = <&foo_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
foo {
|
||||
remote-endpoint = <&bar_con>; // misplaced remote-endpoint property
|
||||
port {
|
||||
foo_con: endpoint {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
bad_endpoint: port-a@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
endpoint@d0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <0xdeadbeef>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
interrupt-parent = <&intc>;
|
||||
intc: interrupt-controller {
|
||||
#interrupt-cells = <3>;
|
||||
};
|
||||
|
||||
node {
|
||||
interrupts = <1>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
interrupt-parent = <&intc>;
|
||||
intc: interrupt-controller {
|
||||
#interrupt-cells = <3>;
|
||||
interrupt-controller;
|
||||
};
|
||||
|
||||
node {
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map = <1 &intc 1 2 3>;
|
||||
interrupt-map-mask = <0 0>;
|
||||
|
||||
child {
|
||||
interrupts = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue