├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── ast27x0
├── Makefile
├── README.md
├── bootrom.ld
├── image.c
├── include
│ ├── io.h
│ ├── uart.h
│ └── uart_console.h
├── start.S
├── uart_aspeed.c
└── uart_console.c
├── lib
├── libc
│ └── minimal
│ │ ├── include
│ │ └── string.h
│ │ └── source
│ │ └── string
│ │ └── string.c
└── libfdt
│ ├── .gitignore
│ ├── Makefile.libfdt
│ ├── TODO
│ ├── fdt.c
│ ├── fdt.h
│ ├── 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
│ ├── libfdt.h
│ ├── libfdt_env.h
│ ├── libfdt_internal.h
│ ├── meson.build
│ ├── sbom.cdx.json
│ └── version.lds
├── npcm7xx
├── Makefile
├── README.md
├── bootrom.ld
├── image.c
└── start.S
└── npcm8xx
├── Makefile
├── README.md
├── bootrom.ld
├── image.c
└── start.S
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | .*.swp
3 | *.o
4 | *.bin
5 | *.elf
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement (CLA). You (or your employer) retain the copyright to your
10 | contribution; this simply gives us permission to use and redistribute your
11 | contributions as part of the project. Head over to
12 | to see your current agreements on file or
13 | to sign a new one.
14 |
15 | You generally only need to submit a CLA once, so if you've already submitted one
16 | (even if it was for a different project), you probably don't need to do it
17 | again.
18 |
19 | ## Code reviews
20 |
21 | All submissions, including submissions by project members, require review. We
22 | use GitHub pull requests for this purpose. Consult
23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
24 | information on using pull requests.
25 |
26 | ## Community Guidelines
27 |
28 | This project follows
29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Virtual Boot ROM for NPCM and ASPEED SoCs
2 |
3 | This repository contains simple Boot ROMs for Nuvoton and ASPEED based
4 | BMC images that are intended to be used by [QEMU](http://www.qemu.org)
5 | when emulating NPCM and ASPEED based machines.
6 |
7 | ## Subdirectories
8 |
9 | npcm7xx: This subdir contains Boot ROM for NPCM7XX, a 32-bit ARM image.
10 | npcm8xx: This subdir contains Boot ROM for NPCM8XX, a 64-bit ARM image.
11 | ast27x0: This subdir contains Boot ROM for AST27X0, a 64-bit ARM image.
12 |
--------------------------------------------------------------------------------
/ast27x0/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2025 Google LLC
2 | # Copyright ASPEED Technology Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | CROSS_COMPILE ?= aarch64-linux-gnu-
17 |
18 | CC ?= $(CROSS_COMPILE)gcc
19 | OBJCOPY ?= $(CROSS_COMPILE)objcopy
20 | OBJDUMP ?= $(CROSS_COMPILE)objdump
21 |
22 | GIT_VERSION := git-$(shell git rev-parse --short HEAD)
23 |
24 | # Why we use -no-pie -fno-pic in bare-metal builds:
25 | #
26 | # By default, modern GCC compilers enable PIE (Position-Independent Executable)
27 | # and PIC (Position-Independent Code) to improve security and relocation
28 | # flexibility.
29 | #
30 | # However, for raw binary (.bin) firmware and bootloaders:
31 | # - We don't have a dynamic loader or relocation mechanism.
32 | # - Function pointers must point to fixed physical addresses.
33 | #
34 | # These flags ensure that:
35 | # - Function calls and pointers are encoded as absolute/static addresses.
36 | # - The compiler does not emit references to the GOT (Global Offset Table),
37 | # which would be missing in a .bin context.
38 | #
39 | # Without these flags, function pointers in .data may be NULL or invalid at
40 | # runtime.
41 | CFLAGS = -Os -Wall -Wextra -g -mcpu=cortex-a35 -fno-stack-protector \
42 | -no-pie -fno-pic \
43 | -I ./include -I ../lib/libfdt -I ../lib/libc/minimal/include
44 | CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
45 | ASFLAGS = $(CFLAGS) -Wa,-mcpu=cortex-a35
46 | LDSCRIPT = bootrom.ld
47 | MAPFILE = bootrom.map
48 | LDFLAGS = -Wl,--build-id=none -static -nostdlib -T $(LDSCRIPT) -Wl,-Map=$(MAPFILE)
49 |
50 | OBJS := start.o image.o uart_aspeed.o uart_console.o \
51 | ../lib/libc/minimal/source/string/string.o \
52 | ../lib/libfdt/fdt.o ../lib/libfdt/fdt_ro.o
53 |
54 | .PHONY: all clean
55 | all: ast27x0_bootrom.bin ast27x0_bootrom.asm
56 |
57 | clean:
58 | rm -f *.o *.bin *.elf *.asm *.map ../lib/libfdt/*.o \
59 | ../lib/libc/minimal/source/string/*.o
60 |
61 | ast27x0_bootrom.bin: ast27x0_bootrom.elf
62 | $(OBJCOPY) -O binary $< $@
63 |
64 | ast27x0_bootrom.asm: ast27x0_bootrom.elf
65 | $(OBJDUMP) -S $< > $@
66 |
67 | ast27x0_bootrom.elf: $(OBJS) $(LDSCRIPT)
68 | $(CC) -o $@ $(LDFLAGS) $(OBJS)
69 |
--------------------------------------------------------------------------------
/ast27x0/README.md:
--------------------------------------------------------------------------------
1 | # Virtual Boot ROM for AST27x0 SoCs
2 |
3 | This is not an officially supported Google product.
4 |
5 | This is a super simple Boot ROM that is intended to be used as a `-bios` image
6 | for [QEMU](http://www.qemu.org/) when emulating an AST27x0-based machine.
7 |
8 | ## Building
9 |
10 | If you have a 64-bit ARM compiler installed as `aarch64-linux-gnu-gcc`, simply run
11 | `make`.
12 |
13 | If your ARM compiler has a different name, you'll need to override the
14 | `CROSS_COMPILE` prefix, e.g. like this:
15 |
16 | ```
17 | make CROSS_COMPILE=aarch64-linux-gnueabi-
18 | ```
19 |
20 | If either case is successful, a `ast27x0_bootrom.bin` file will be produced.
21 |
22 | ## Using
23 |
24 | The Boot ROM image may be passed to a QEMU system emulator using the `-bios` option. For example like this:
25 |
26 | ```
27 | qemu-system-aarch64 -machine ast2700a1-evb -nographic \
28 | -bios ${IMAGES}/ast27x0_bootrom.bin \
29 | -drive file=${IMAGES}/image-bmc,format=raw,if=mtd \
30 | -snapshot
31 | ```
32 |
33 | ## Limitations
34 |
--------------------------------------------------------------------------------
/ast27x0/bootrom.ld:
--------------------------------------------------------------------------------
1 | /*
2 | * Linker script for the Boot ROM.
3 | *
4 | * Copyright 2025 Google LLC
5 | * Copyright (C) ASPEED Technology Inc.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | MEMORY
21 | {
22 | rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K
23 | ram (arwx) : ORIGIN = 0x10000000, LENGTH = 128K
24 | }
25 |
26 | SECTIONS
27 | {
28 | /* Vectors are loaded into ROM */
29 | .text.vectors : {
30 | __vectors_vma = .;
31 | *(.text.vectors)
32 | . = 0x100;
33 | __vectors_end = .;
34 | } >rom
35 | /* The rest of the code follows the vectors.
36 | * The main code and read-only data, located in ROM
37 | */
38 | .text : {
39 | __text = .;
40 | *(.text .text.*)
41 | . = ALIGN(32);
42 | *(.rodata .rodata.*)
43 | . = ALIGN(32);
44 | __etext = .;
45 | } >rom
46 | /*
47 | * Initialized data section
48 | * Data follows the code in ROM, and is copied in RAM.
49 | */
50 | .data : {
51 | __data = .;
52 | *(.data .data.*)
53 | . = ALIGN(32);
54 | __edata = .;
55 | } >ram AT>rom
56 | __data_loadaddr = LOADADDR(.data);
57 |
58 | /* Zero-initialized data (BSS) lives in RAM, after the data section. */
59 | .bss : {
60 | __bss_start = .;
61 | *(.bss .bss.*)
62 | . = ALIGN(32);
63 | *(COMMON)
64 | . = ALIGN(32);
65 | __bss_end = .;
66 | __end = .;
67 | } >ram
68 | }
69 |
--------------------------------------------------------------------------------
/ast27x0/image.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Boot image parsing and loading.
3 | *
4 | * Copyright 2025 Google LLC
5 | * Copyright (C) ASPEED Technology Inc.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #define DEBUG 0
26 | #define DRAM_ADDR 0x400000000ULL
27 | #define FMCCS0 0x100000000ULL
28 | #define UART12 0x14C33B00
29 |
30 | #define FIT_SEARCH_START (FMCCS0)
31 | #define FIT_SEARCH_END (FMCCS0 + 0x400000)
32 | #define FIT_SEARCH_STEP 0x10000
33 |
34 | extern void panic(const char *);
35 |
36 | /*
37 | * This global struct is explicitly initialized, so it is placed in the .data
38 | * section. The function pointer (uputc) is set to uart_aspeed_poll_out at
39 | * compile time, which ensures its address is embedded in the final binary.
40 | *
41 | * If the struct were uninitialized, it would be placed in the .bss section,
42 | * and the function pointer would default to zero (NULL).
43 | * This distinction is especially important in bare-metal or ROM-based
44 | * environments, where only initialized data (.data) is included in the final
45 | * .bin image.
46 | *
47 | * To prevent this:
48 | * - Use initialized globals (ensures placement in .data)
49 | *
50 | * In embedded systems, especially when generating stripped-down .bin
51 | * firmware, it's critical to ensure that essential function pointers are
52 | * preserved explicitly.
53 | */
54 | static struct uart_console ucons = {
55 | .uputc = uart_aspeed_poll_out
56 | };
57 |
58 | static const char *splash_screen =
59 | " _ ______ ____ ____ __________ ____ __ ___ ___ ______________ ______ ______\n"
60 | "| | / / __ )/ __ \\/ __ \\/_ __/ __ \\/ __ \\/ |/ / / | / ___/_ __/__ \\/__ / |/ / __ \\\n"
61 | "| | / / __ / / / / / / / / / / /_/ / / / / /|_/ /_____/ /| | \\__ \\ / / __/ / / /| / / / /\n"
62 | "| |/ / /_/ / /_/ / /_/ / / / / _, _/ /_/ / / / /_____/ ___ |___/ // / / __/ / // / /_/ /\n"
63 | "|___/_____/\\____/\\____/ /_/ /_/ |_|\\____/_/ /_/ /_/ |_/____//_/ /____/ /_//_/|_\\____/\n"
64 | "\n";
65 |
66 | static void print_build_info()
67 | {
68 | uprintf("%s", splash_screen);
69 | uprintf("Build Date : %s %s\n", __DATE__, __TIME__);
70 | uprintf("FW Version : %s\n", GIT_VERSION);
71 | uprintf("\n");
72 | }
73 |
74 | /*
75 | * Remap 32-bit BootMCU load address to 64-bit Cortex-A35 DRAM address.
76 | * BootMCU loads the U-BOOT FIT image to a 32-bit address (e.g. 0x80000000),
77 | * but Cortex-A35 accesses DRAM starting at DRAM_ADDR (e.g. 0x400000000).
78 | */
79 | static inline uint64_t convert_mcu_addr_to_arm_dram(uint64_t mcu_load_addr)
80 | {
81 | /*
82 | * If address is below 0x80000000, it's a physical address in SRAM.
83 | * No remapping is needed, return as-is.
84 | */
85 | if (mcu_load_addr < 0x80000000) {
86 | return mcu_load_addr;
87 | }
88 |
89 | return DRAM_ADDR + ((uint64_t)mcu_load_addr - 0x80000000);
90 | }
91 |
92 | static uint64_t read_fit_address(const void *fdt, int node,
93 | const char *prop_name)
94 | {
95 | const char *node_name;
96 | const fdt32_t *prop;
97 | uint32_t addr_upper;
98 | uint32_t addr_lower;
99 | int len;
100 |
101 | if (!prop_name) {
102 | uprintf("Invalid: prop_name is NULL!\n");
103 | return 0;
104 | }
105 |
106 | node_name = fdt_get_name(fdt, node, NULL);
107 | if (!node_name) {
108 | node_name = "";
109 | }
110 |
111 | prop = fdt_getprop(fdt, node, prop_name, &len);
112 | if (!prop) {
113 | uprintf("[%s] %s: property not found\n", node_name, prop_name);
114 | return 0;
115 | }
116 |
117 | if (len == 4) {
118 | return (uint64_t)fdt32_to_cpu(prop[0]);
119 | } else if (len == 8) {
120 | addr_upper = fdt32_to_cpu(prop[0]);
121 | addr_lower = fdt32_to_cpu(prop[1]);
122 | return ((uint64_t)addr_upper << 32) | addr_lower;
123 | } else {
124 | uprintf("[%s] %s: invalid length (%d bytes, expected 4 or 8)\n",
125 | node_name, prop_name, len);
126 | return 0;
127 | }
128 | }
129 |
130 | #if DEBUG
131 | static void dump_fit_image(const void *fit_blob)
132 | {
133 | uintptr_t data_offset;
134 | int images_offset;
135 | const void *data;
136 | int node_offset;
137 | const char *str;
138 | uint64_t addr;
139 | int data_len;
140 |
141 | images_offset = fdt_path_offset(fit_blob, "/images");
142 | if (images_offset < 0) {
143 | uprintf("No /images node found\n");
144 | return;
145 | }
146 |
147 | fdt_for_each_subnode(node_offset, fit_blob, images_offset) {
148 | str = fdt_get_name(fit_blob, node_offset, NULL);
149 | uprintf("Image node: %s\n", str);
150 |
151 | str = fdt_getprop(fit_blob, node_offset, "description", NULL);
152 | if (str) {
153 | uprintf(" description: %s\n", str);
154 | }
155 |
156 | addr = read_fit_address(fit_blob, node_offset, "load");
157 | if (addr) {
158 | uprintf(" load address: 0x%016llx\n", (unsigned long long)addr);
159 | }
160 |
161 | addr = read_fit_address(fit_blob, node_offset, "entry");
162 | if (addr) {
163 | uprintf(" entry point: 0x%016llx\n", (unsigned long long)addr);
164 | }
165 |
166 | str = fdt_getprop(fit_blob, node_offset, "type", NULL);
167 | if (str) {
168 | uprintf(" type: %s\n", str);
169 | }
170 |
171 | str = fdt_getprop(fit_blob, node_offset, "os", NULL);
172 | if (str) {
173 | uprintf(" os: %s\n", str);
174 | }
175 |
176 | str = fdt_getprop(fit_blob, node_offset, "arch", NULL);
177 | if (str) {
178 | uprintf(" arch: %s\n", str);
179 | }
180 |
181 | str = fdt_getprop(fit_blob, node_offset, "compression", NULL);
182 | if (str) {
183 | uprintf(" compression: %s\n", str);
184 | }
185 |
186 | data = fdt_getprop(fit_blob, node_offset, "data", &data_len);
187 | if (data && data_len > 0) {
188 | data_offset = (uintptr_t)data - (uintptr_t)fit_blob;
189 | uprintf(" data: %d bytes @ offset 0x%lx\n",
190 | data_len, data_offset);
191 | } else {
192 | uprintf(" data: not found or empty\n");
193 | }
194 | }
195 | }
196 | #endif
197 |
198 | /*
199 | * Loads the "U-Boot" image from the FIT and returns its end address in memory.
200 | *
201 | * This function must be called before loading other images because some images,
202 | * such as "fdt", may not have an explicit "load" property defined. In those
203 | * cases, their load address is determined by placing them immediately after the
204 | * u-boot image in memory.
205 | *
206 | * Loading U-Boot first ensures a well-defined memory layout baseline for
207 | * subsequent images that rely on relative placement.
208 | */
209 | static uint64_t load_uboot_image(const void *fit_blob)
210 | {
211 | uintptr_t data_offset;
212 | uint64_t load_addr;
213 | uint64_t dram_addr;
214 | int images_offset;
215 | const void *data;
216 | const char *name;
217 | int node_offset;
218 | int data_len;
219 |
220 | images_offset = fdt_path_offset(fit_blob, "/images");
221 | if (images_offset < 0) {
222 | uprintf("No /images node found\n");
223 | return 0;
224 | }
225 |
226 | fdt_for_each_subnode(node_offset, fit_blob, images_offset) {
227 | name = fdt_get_name(fit_blob, node_offset, NULL);
228 | if (strcmp(name, "uboot") != 0) {
229 | continue;
230 | }
231 |
232 | load_addr = read_fit_address(fit_blob, node_offset, "load");
233 | if (!load_addr) {
234 | uprintf("[%s] has no load address!\n", name);
235 | return 0;
236 | }
237 |
238 | uprintf("[%s] load address: 0x%lx\n", name,
239 | (unsigned long long)load_addr);
240 |
241 | data = fdt_getprop(fit_blob, node_offset, "data", &data_len);
242 | if (!data || data_len <= 0) {
243 | uprintf("[%s] has no data!\n", name);
244 | return 0;
245 | }
246 |
247 | data_offset = (uintptr_t)data - (uintptr_t)fit_blob;
248 | uprintf("[%s] load end address: 0x%lx\n", name,
249 | (unsigned long long)(load_addr + data_len));
250 | uprintf("[%s] data: %d bytes @ offset 0x%lx\n", name, data_len,
251 | data_offset);
252 | dram_addr = convert_mcu_addr_to_arm_dram(load_addr);
253 | uprintf("[%s] loading %d bytes to 0x%lx ... ", name, data_len,
254 | (unsigned long long)dram_addr);
255 | memcpy((void *)(uintptr_t)dram_addr, data, data_len);
256 | uprintf("done\n");
257 | return load_addr + data_len;
258 | }
259 |
260 | uprintf("[uboot] not found in FIT\n");
261 | return 0;
262 | }
263 |
264 | /*
265 | * Loads all images from the FIT except "U-Boot".
266 | *
267 | * If an image has a "load" property, its value is remapped to the 64-bit DRAM
268 | * address space and used directly. If the image is "fdt" and does not have a
269 | * "load" property, it is placed immediately after the U-Boot image, using the
270 | * given uboot_end address as its load location.
271 | *
272 | * This function depends on uboot_end being correctly computed in advance by
273 | * load_uboot_image(). This ensures a clean and predictable memory layout even
274 | * if some images do not define explicit load addresses in the FIT.
275 | */
276 | static void load_other_fit_images(const void *fit_blob, uint64_t uboot_end,
277 | uint64_t *dest_addr)
278 | {
279 | uintptr_t data_offset;
280 | uint64_t load_addr;
281 | uint64_t dram_addr;
282 | int images_offset;
283 | const void *data;
284 | const char *name;
285 | int node_offset;
286 | int data_len;
287 |
288 | images_offset = fdt_path_offset(fit_blob, "/images");
289 | if (images_offset < 0) {
290 | uprintf("No /images node found\n");
291 | return;
292 | }
293 |
294 | fdt_for_each_subnode(node_offset, fit_blob, images_offset) {
295 | name = fdt_get_name(fit_blob, node_offset, NULL);
296 |
297 | /* Skip U-Boot, which should already be loaded */
298 | if (strcmp(name, "uboot") == 0) {
299 | continue;
300 | }
301 |
302 | data = fdt_getprop(fit_blob, node_offset, "data", &data_len);
303 | if (!data || data_len <= 0) {
304 | uprintf("[%s] skip: no data\n", name);
305 | continue;
306 | }
307 |
308 | load_addr = read_fit_address(fit_blob, node_offset, "load");
309 |
310 | if (load_addr) {
311 | uprintf("[%s] load address: 0x%lx\n", name,
312 | (unsigned long long)load_addr);
313 | /* Image has explicit load address, remap for ARM DRAM view */
314 | dram_addr = convert_mcu_addr_to_arm_dram(load_addr);
315 | /* The next image to jump to is BL31 (Trusted Firmware-A) */
316 | if (strcmp(name, "atf") == 0) {
317 | *dest_addr = dram_addr;
318 | }
319 | } else if (strcmp(name, "fdt") == 0 && uboot_end) {
320 | /* fdt has no load address, fallback to uboot_end */
321 | load_addr = uboot_end;
322 | dram_addr = convert_mcu_addr_to_arm_dram(load_addr);
323 | uprintf("[%s] no load addr, fallback to u-boot end: 0x%lx\n",
324 | name, (unsigned long long)load_addr);
325 | } else {
326 | uprintf("[%s] skip: no load address and no fallback\n", name);
327 | continue;
328 | }
329 |
330 | data_offset = (uintptr_t)data - (uintptr_t)fit_blob;
331 | uprintf("[%s] data: %d bytes @ offset 0x%lx\n",
332 | name, data_len, data_offset);
333 | uprintf("[%s] loading %d bytes to 0x%lx ... ",
334 | name, data_len, (unsigned long long)dram_addr);
335 | memcpy((void *)(uintptr_t)dram_addr, data, data_len);
336 | uprintf("done\n");
337 | }
338 | }
339 |
340 | static const void *find_fit_image(uint64_t start_addr, uint64_t end_addr,
341 | uint64_t search_step)
342 | {
343 | const void *ptr = NULL;
344 | int total_size;
345 | uint64_t addr;
346 |
347 | if (search_step == 0) {
348 | uprintf("search_step cannot be zero.\n");
349 | return NULL;
350 | }
351 |
352 | for (addr = start_addr; addr < end_addr; addr += search_step) {
353 | ptr = (const void *)(uintptr_t)addr;
354 |
355 | if (fdt_check_header(ptr) == 0) {
356 | total_size = fdt_totalsize(ptr);
357 | uprintf("Found valid FIT image at 0x%lx (size: 0x%x bytes)\n",
358 | addr, total_size);
359 | return ptr;
360 | }
361 | }
362 |
363 | uprintf("No valid FIT image found in range 0x%lx - 0x%lx (step: 0x%lx)\n",
364 | start_addr, end_addr, search_step);
365 |
366 | return NULL;
367 | }
368 |
369 | uint64_t load_boot_image(void)
370 | {
371 | uint64_t bl31_addr = 0;
372 | const void *fit_blob;
373 | uint64_t uboot_end;
374 |
375 | uart_aspeed_init(UART12);
376 | uart_console_register(&ucons);
377 |
378 | print_build_info();
379 | fit_blob = find_fit_image(FIT_SEARCH_START,
380 | FIT_SEARCH_END,
381 | FIT_SEARCH_STEP);
382 | if (!fit_blob) {
383 | panic("");
384 | }
385 |
386 | #if DEBUG
387 | dump_fit_image(fit_blob);
388 | #endif
389 |
390 | uboot_end = load_uboot_image(fit_blob);
391 |
392 | if (!uboot_end) {
393 | panic("");
394 | }
395 |
396 | load_other_fit_images(fit_blob, uboot_end, &bl31_addr);
397 |
398 | if (!bl31_addr) {
399 | uprintf("Error: BL31 (Trusted Firmware-A) not found, halting.\n");
400 | panic("");
401 | }
402 | uprintf("\nJumping to BL31 (Trusted Firmware-A) at 0x%lx\n\n",
403 | bl31_addr);
404 | return bl31_addr;
405 | }
406 |
--------------------------------------------------------------------------------
/ast27x0/include/io.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2025 ASPEED Technology Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef __AST27X0_INCLUDE_IO_H__
18 | #define __AST27X0_INCLUDE_IO_H__
19 |
20 | #define readb(addr) (*(volatile unsigned char *)(addr))
21 | #define readw(addr) (*(volatile unsigned short *)(addr))
22 | #define readl(addr) (*((volatile unsigned int *)(addr)))
23 | #define readq(addr) (*((volatile unsigned long long *)(addr)))
24 |
25 | #define writeb(value, addr) (*(volatile unsigned char *)(addr) = (unsigned char)value)
26 | #define writew(value, addr) (*(volatile unsigned short *)(addr) = (unsigned short)value)
27 | #define writel(value, addr) (*(volatile unsigned int *)(addr) = (unsigned int)(value))
28 | #define writeq(value, addr) (*(volatile unsigned long long *)(addr) = (unsigned long long)(value))
29 |
30 | #endif /* __AST27X0_INCLUDE_IO_H__ */
31 |
--------------------------------------------------------------------------------
/ast27x0/include/uart.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2025 ASPEED
3 | * Copyright (c) 2018-2019 Nordic Semiconductor ASA
4 | * Copyright (c) 2015 Wind River Systems, Inc.
5 | *
6 | * SPDX-License-Identifier: Apache-2.0
7 | */
8 |
9 | /**
10 | * @file
11 | * @brief Public APIs for UART drivers
12 | */
13 |
14 | #ifndef __AST27X0_INCLUDE_UART_H__
15 | #define __AST27X0_INCLUDE_UART_H__
16 |
17 | /**
18 | * @brief UART Interface
19 | * @defgroup uart_interface UART Interface
20 | * @since 1.0
21 | * @version 1.0.0
22 | * @ingroup io_interfaces
23 | * @{
24 | */
25 |
26 | #ifdef __cplusplus
27 | extern "C" {
28 | #endif
29 |
30 | /** @brief Parity modes */
31 | enum uart_config_parity {
32 | UART_CFG_PARITY_NONE, /**< No parity */
33 | UART_CFG_PARITY_ODD, /**< Odd parity */
34 | UART_CFG_PARITY_EVEN, /**< Even parity */
35 | UART_CFG_PARITY_MARK, /**< Mark parity */
36 | UART_CFG_PARITY_SPACE, /**< Space parity */
37 | };
38 |
39 | /** @brief Number of stop bits. */
40 | enum uart_config_stop_bits {
41 | UART_CFG_STOP_BITS_0_5, /**< 0.5 stop bit */
42 | UART_CFG_STOP_BITS_1, /**< 1 stop bit */
43 | UART_CFG_STOP_BITS_1_5, /**< 1.5 stop bits */
44 | UART_CFG_STOP_BITS_2, /**< 2 stop bits */
45 | };
46 |
47 | /** @brief Number of data bits. */
48 | enum uart_config_data_bits {
49 | UART_CFG_DATA_BITS_5, /**< 5 data bits */
50 | UART_CFG_DATA_BITS_6, /**< 6 data bits */
51 | UART_CFG_DATA_BITS_7, /**< 7 data bits */
52 | UART_CFG_DATA_BITS_8, /**< 8 data bits */
53 | UART_CFG_DATA_BITS_9, /**< 9 data bits */
54 | };
55 |
56 | /**
57 | * @brief Hardware flow control options.
58 | *
59 | * With flow control set to none, any operations related to flow control
60 | * signals can be managed by user with uart_line_ctrl functions.
61 | * In other cases, flow control is managed by hardware/driver.
62 | */
63 | enum uart_config_flow_control {
64 | UART_CFG_FLOW_CTRL_NONE,
65 | UART_CFG_FLOW_CTRL_RTS_CTS,
66 | UART_CFG_FLOW_CTRL_DTR_DSR,
67 | UART_CFG_FLOW_CTRL_RS485,
68 | };
69 |
70 | /**
71 | * @brief UART controller configuration structure
72 | */
73 | struct uart_config {
74 | uint32_t baudrate; /**< Baudrate setting in bps */
75 | uint8_t parity; /**< Parity bit, use @ref uart_config_parity */
76 | uint8_t stop_bits; /**< Stop bits, use @ref uart_config_stop_bits */
77 | uint8_t data_bits; /**< Data bits, use @ref uart_config_data_bits */
78 | uint8_t flow_ctrl; /**< Flow control setting, use @ref uart_config_flow_control */
79 | };
80 |
81 | void uart_aspeed_poll_out(unsigned char c);
82 | int uart_aspeed_init(uintptr_t base);
83 |
84 | #ifdef __cplusplus
85 | }
86 | #endif
87 |
88 | #endif /* __AST27X0_INCLUDE_UART_H__ */
89 |
--------------------------------------------------------------------------------
/ast27x0/include/uart_console.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2025 ASPEED Technology Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef __AST27X0_INCLUDE_UART_CONSOLE_H__
18 | #define __AST27X0_INCLUDE_UART_CONSOLE_H__
19 |
20 | struct uart_console {
21 | void (*uputc)(unsigned char c);
22 | };
23 |
24 | int uart_console_register(struct uart_console *ucons);
25 | int uprintf(const char *fmt, ...);
26 |
27 | #endif /* __AST27X0_INCLUDE_UART_CONSOLE_H__ */
28 |
--------------------------------------------------------------------------------
/ast27x0/start.S:
--------------------------------------------------------------------------------
1 | /*
2 | * Top-level entry points to the Boot ROM. This includes:
3 | * - Reset, exception and interrupt vectors.
4 | * - C run-time initialization.
5 | * - Secondary CPU boot code.
6 | *
7 | * Copyright 2025 Google LLC
8 | * Copyright (C) ASPEED Technology Inc.
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 |
23 | #define KiB (1024)
24 |
25 | #define SRAM_SIZE (128 * KiB)
26 |
27 | .section .bss
28 |
29 | .global next_boot_addr
30 | .type next_boot_addr, %object
31 | next_boot_addr:
32 | .skip 8
33 | .size next_boot_addr, . - next_boot_addr
34 |
35 | .section .text.vectors, "ax"
36 |
37 | .global _start
38 | .type _start, %function
39 | _start:
40 | b reset
41 | . = 0x04
42 | b undefined_instruction
43 | . = 0x08
44 | b software_interrupt
45 | . = 0x0c
46 | b prefetch_abort
47 | . = 0x10
48 | b data_abort
49 | . = 0x18
50 | b interrupt
51 | . = 0x1c
52 | b fast_interrupt
53 |
54 | undefined_instruction:
55 | mov x0, #1
56 | b handle_exception
57 |
58 | software_interrupt:
59 | mov x0, #2
60 | b handle_exception
61 |
62 | prefetch_abort:
63 | mov x0, #3
64 | b handle_exception
65 |
66 | data_abort:
67 | mov x0, #4
68 | b handle_exception
69 |
70 | interrupt:
71 | mov x0, #6
72 | b handle_exception
73 |
74 | fast_interrupt:
75 | mov x0, #7
76 | b handle_exception
77 |
78 | vectors_end:
79 |
80 | .text
81 | .align 2
82 | handle_exception:
83 |
84 | .global panic
85 | .type panic, %function
86 | panic:
87 | 1: wfi
88 | b 1b
89 | .size panic, . - panic
90 |
91 | .type reset, %function
92 | reset:
93 | mov x0, #0
94 | // Determine the current core ID using MPIDR_EL1.
95 | // If it's core 0, jump to CPU0 initialization routine.
96 | // Otherwise, wait for CPU0 to publish the boot entry point.
97 | mrs x1, MPIDR_EL1
98 | and x1, x1, #0x03
99 | cbz x1, cpu0_init
100 |
101 | // Non-CPU0 cores wait here until CPU0 sets the next_boot_addr.
102 | // Once it's non-zero, jump to that address.
103 | 1:
104 | ldr x2, =next_boot_addr
105 | ldr x3, [x2]
106 | cbnz x3, 2f
107 | wfe
108 | b 1b
109 |
110 | // Perform synchronization before branching to boot image.
111 | 2:
112 | dsb sy
113 | isb
114 | br x3
115 |
116 | // Should never reach here unless boot entry is invalid.
117 | b panic
118 | .size reset, . - reset
119 |
120 | .type cpu0_init, %function
121 | cpu0_init:
122 | // Set up stack pointer to top of SRAM (with 16-byte safety margin).
123 | // This ensures the stack is within the SRAM region and avoids overflow.
124 | ldr x1, sram_base_addr
125 | mov x2, #SRAM_SIZE
126 | sub x2, x2, #0x10 // leave 16-byte safety margin
127 | add sp, x1, x2 // SP = base + size - 0x10
128 |
129 | // Copy initialized .data section from ROM (LMA) to RAM (VMA)
130 | ldr x3, =__data_loadaddr // source address in ROM
131 | ldr x4, =__data // destination address in RAM
132 | ldr x5, =__edata // end of .data in RAM
133 | copy_data_loop:
134 | cmp x4, x5
135 | b.ge data_copy_done
136 | ldr w6, [x3], #4 // load word from ROM and post-increment
137 | str w6, [x4] // store word to RAM
138 | add x4, x4, #4 // move to next word
139 | b copy_data_loop
140 | data_copy_done:
141 | // Zero-initialize the .bss section in RAM
142 | ldr x4, =__bss_start
143 | ldr x5, =__bss_end
144 | clear_bss_loop:
145 | cmp x4, x5
146 | b.ge bss_clear_done
147 | mov w6, #0
148 | str w6, [x4] // store zero to memory
149 | add x4, x4, #4 // move to next word
150 | b clear_bss_loop
151 | bss_clear_done:
152 | // Load the boot image.
153 | // Returns the entry point in x0
154 | bl load_boot_image
155 |
156 | // Write entry point to shared memory for other CPUs
157 | // and send an event to wake them up from WFE.
158 | // x0 now holds the boot image entry point
159 | ldr x2, =next_boot_addr
160 | str x0, [x2]
161 | dsb st
162 | sev
163 |
164 | // Synchronize memory and pipeline before jumping to the boot image.
165 | dsb sy
166 | isb
167 | br x0
168 |
169 | // Should never reach here unless something goes wrong.
170 | b panic
171 | .size cpu0_init, . - cpu0_init
172 |
173 | .section .rodata
174 | .type sram_base_addr, %object
175 | sram_base_addr:
176 | .dword 0x10000000
177 | .size sram_base_addr, . - sram_base_addr
178 |
179 |
--------------------------------------------------------------------------------
/ast27x0/uart_aspeed.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2025 ASPEED Technology Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | #define BITS_PER_LONG (sizeof(unsigned long) * 8)
22 | #define GENMASK(h, l) \
23 | (((~0UL) >> (BITS_PER_LONG - ((h) - (l) + 1))) << (l))
24 |
25 | #define BIT(x) (1UL << (x))
26 |
27 | /* UART registers */
28 | #define UART_THR 0x00
29 | #define UART_DLL 0x00
30 | #define UART_DLH 0x04
31 | #define UART_FCR 0x08
32 | #define UART_LCR 0x0c
33 | #define UART_LSR 0x14
34 |
35 | /* UART_FCR */
36 | #define UART_FCR_TRIG_MASK GENMASK(7, 6)
37 | #define UART_FCR_TRIG_SHIFT 6
38 | #define UART_FCR_TX_RST BIT(2)
39 | #define UART_FCR_RX_RST BIT(1)
40 | #define UART_FCR_EN BIT(0)
41 |
42 | /* UART_LCR */
43 | #define UART_LCR_DLAB BIT(7)
44 | #define UART_LCR_PARITY_MODE BIT(4)
45 | #define UART_LCR_PARITY_EN BIT(3)
46 | #define UART_LCR_STOP BIT(2)
47 | #define UART_LCR_CLS_MASK GENMASK(1, 0)
48 | #define UART_LCR_CLS_SHIFT 0
49 |
50 | /* UART_LSR */
51 | #define UART_LSR_THRE BIT(5)
52 |
53 | struct uart_aspeed_config {
54 | uintptr_t base;
55 | };
56 |
57 | struct uart_aspeed_config uart_dev_cfg;
58 |
59 | static int uart_aspeed_configure(struct uart_aspeed_config *dev_cfg,
60 | const struct uart_config *uart_cfg)
61 | {
62 | uint32_t clk_rate;
63 | uint32_t divisor;
64 | uint32_t reg;
65 | int rc = 0;
66 |
67 | if (!dev_cfg || !uart_cfg) {
68 | return -1;
69 | }
70 |
71 | clk_rate = 1846153;
72 | divisor = clk_rate / (16 * uart_cfg->baudrate);
73 |
74 | reg = readl(dev_cfg->base + UART_LCR);
75 | reg |= UART_LCR_DLAB;
76 | writel(reg, dev_cfg->base + UART_LCR);
77 |
78 | writel(divisor & 0xf, dev_cfg->base + UART_DLL);
79 | writel(divisor >> 8, dev_cfg->base + UART_DLH);
80 |
81 | reg &= ~(UART_LCR_DLAB | UART_LCR_CLS_MASK | UART_LCR_STOP);
82 |
83 | switch (uart_cfg->data_bits) {
84 | case UART_CFG_DATA_BITS_5:
85 | reg |= ((0x0 << UART_LCR_CLS_SHIFT) & UART_LCR_CLS_MASK);
86 | break;
87 | case UART_CFG_DATA_BITS_6:
88 | reg |= ((0x1 << UART_LCR_CLS_SHIFT) & UART_LCR_CLS_MASK);
89 | break;
90 | case UART_CFG_DATA_BITS_7:
91 | reg |= ((0x2 << UART_LCR_CLS_SHIFT) & UART_LCR_CLS_MASK);
92 | break;
93 | case UART_CFG_DATA_BITS_8:
94 | reg |= ((0x3 << UART_LCR_CLS_SHIFT) & UART_LCR_CLS_MASK);
95 | break;
96 | default:
97 | rc = -1;
98 | goto out;
99 | }
100 |
101 | switch (uart_cfg->stop_bits) {
102 | case UART_CFG_STOP_BITS_1:
103 | reg &= ~(UART_LCR_STOP);
104 | break;
105 | case UART_CFG_STOP_BITS_2:
106 | reg |= UART_LCR_STOP;
107 | break;
108 | default:
109 | rc = -1;
110 | goto out;
111 | }
112 |
113 | switch (uart_cfg->parity) {
114 | case UART_CFG_PARITY_NONE:
115 | reg &= ~(UART_LCR_PARITY_EN);
116 | break;
117 | case UART_CFG_PARITY_ODD:
118 | reg |= UART_LCR_PARITY_EN;
119 | break;
120 | case UART_CFG_PARITY_EVEN:
121 | reg |= (UART_LCR_PARITY_EN | UART_LCR_PARITY_MODE);
122 | break;
123 | default:
124 | rc = -1;
125 | goto out;
126 | }
127 |
128 | writel(reg, dev_cfg->base + UART_LCR);
129 |
130 | /*
131 | * enable FIFO, generate the interrupt at 8th byte
132 | */
133 | reg = ((0x2 << UART_FCR_TRIG_SHIFT) & UART_FCR_TRIG_MASK) |
134 | UART_FCR_TX_RST |
135 | UART_FCR_RX_RST |
136 | UART_FCR_EN;
137 | writel(reg, dev_cfg->base + UART_FCR);
138 |
139 | out:
140 | return rc;
141 | };
142 |
143 | void uart_aspeed_poll_out(unsigned char c)
144 | {
145 | uint32_t sts;
146 |
147 | do {
148 | sts = readl(uart_dev_cfg.base + UART_LSR);
149 | } while (!(sts & UART_LSR_THRE));
150 |
151 | writel(c, uart_dev_cfg.base + UART_THR);
152 | }
153 |
154 | int uart_aspeed_init(uintptr_t base)
155 | {
156 | struct uart_config uart_cfg;
157 | int rc = 0;
158 |
159 | uart_dev_cfg.base = base;
160 |
161 | uart_cfg.baudrate = 115200;
162 | uart_cfg.parity = UART_CFG_PARITY_NONE;
163 | uart_cfg.stop_bits = UART_CFG_STOP_BITS_1;
164 | uart_cfg.data_bits = UART_CFG_DATA_BITS_8;
165 | uart_cfg.flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
166 |
167 | rc = uart_aspeed_configure(&uart_dev_cfg, &uart_cfg);
168 |
169 | return rc;
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/ast27x0/uart_console.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2025 ASPEED Technology Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | static struct uart_console sys_ucons;
22 |
23 | static void uputc(char c)
24 | {
25 | if (!sys_ucons.uputc) {
26 | return;
27 | }
28 |
29 | if (c == '\n') {
30 | sys_ucons.uputc('\r');
31 | }
32 |
33 | sys_ucons.uputc(c);
34 | }
35 |
36 | static void uputs(const char *s)
37 | {
38 | for (int i = 0; s[i]; i++) {
39 | uputc(s[i]);
40 | }
41 | }
42 |
43 | static void uputx(uint64_t val, int width)
44 | {
45 | char hex[17];
46 | const char *digits = "0123456789abcdef";
47 | int pos = 16;
48 |
49 | hex[pos] = '\0';
50 | if (val == 0 && width == 0) {
51 | uputc('0');
52 | return;
53 | }
54 |
55 | while (val && pos > 0) {
56 | hex[--pos] = digits[val & 0xF];
57 | val >>= 4;
58 | }
59 |
60 | while (16 - pos < width && pos > 0) {
61 | hex[--pos] = '0';
62 | }
63 |
64 | uputs(&hex[pos]);
65 | }
66 |
67 | static void uputd(int64_t val)
68 | {
69 | /* Enough for "-9223372036854775808" */
70 | char buf[21];
71 | int i = 0;
72 | uint64_t uval;
73 |
74 | if (val < 0) {
75 | uputc('-');
76 | uval = (uint64_t)(-val);
77 | } else {
78 | uval = (uint64_t)val;
79 | }
80 |
81 | do {
82 | buf[i++] = '0' + (uval % 10);
83 | uval /= 10;
84 | } while (uval > 0);
85 |
86 | while (i-- > 0) {
87 | uputc(buf[i]);
88 | }
89 | }
90 |
91 | int uprintf(const char *fmt, ...)
92 | {
93 | va_list va;
94 | int count = 0;
95 |
96 | va_start(va, fmt);
97 |
98 | while (*fmt) {
99 | if (*fmt == '%') {
100 | /* Move past '%' */
101 | fmt++;
102 |
103 | /* Handle padding like %016lx */
104 | int width = 0;
105 |
106 | if (*fmt == '0') {
107 | fmt++;
108 | while (*fmt >= '0' && *fmt <= '9') {
109 | width = width * 10 + (*fmt - '0');
110 | fmt++;
111 | }
112 | }
113 |
114 | /* Detect length modifier */
115 | int is_long = 0;
116 | int is_longlong = 0;
117 |
118 | if (*fmt == 'l') {
119 | fmt++;
120 | if (*fmt == 'l') {
121 | is_longlong = 1;
122 | fmt++;
123 | } else {
124 | is_long = 1;
125 | }
126 | }
127 |
128 | if (*fmt == '\0') {
129 | break;
130 | }
131 |
132 | switch (*fmt) {
133 | case 's': {
134 | const char *str = va_arg(va, const char *);
135 | while (*str) {
136 | uputc(*str++);
137 | count++;
138 | }
139 | break;
140 | }
141 | case 'x': {
142 | uint64_t val;
143 | if (is_longlong) {
144 | val = va_arg(va, unsigned long long);
145 | } else if (is_long) {
146 | val = va_arg(va, unsigned long);
147 | } else {
148 | val = va_arg(va, unsigned int);
149 | }
150 | /* Assume uputx handles zero-padding */
151 | uputx(val, width);
152 | break;
153 | }
154 | case 'd': {
155 | int val = va_arg(va, int);
156 | /* Print signed decimal */
157 | uputd(val);
158 | break;
159 | }
160 | case '%': {
161 | uputc('%');
162 | count++;
163 | break;
164 | }
165 | default:
166 | uputc('%');
167 | uputc(*fmt);
168 | count += 2;
169 | break;
170 | }
171 | } else {
172 | uputc(*fmt);
173 | count++;
174 | }
175 |
176 | fmt++;
177 | }
178 |
179 | va_end(va);
180 | return count;
181 | }
182 |
183 | int uart_console_register(struct uart_console *ucons)
184 | {
185 | if (!ucons || sys_ucons.uputc) {
186 | return 1;
187 | }
188 |
189 | sys_ucons.uputc = ucons->uputc;
190 |
191 | return 0;
192 | }
193 |
194 |
--------------------------------------------------------------------------------
/lib/libc/minimal/include/string.h:
--------------------------------------------------------------------------------
1 | /* string.h */
2 |
3 | /*
4 | * Copyright (c) 2014 Wind River Systems, Inc.
5 | * Copyright (C) ASPEED Technology Inc.
6 | *
7 | * SPDX-License-Identifier: Apache-2.0
8 | */
9 |
10 | #ifndef LIB_LIBC_MINIMAL_INCLUDE_STRING_H_
11 | #define LIB_LIBC_MINIMAL_INCLUDE_STRING_H_
12 |
13 | #include
14 |
15 | #ifdef __cplusplus
16 | extern "C" {
17 | #endif
18 |
19 | #ifndef ZRESTRICT
20 | #define ZRESTRICT
21 | #endif
22 |
23 | extern char *strcpy(char *ZRESTRICT d, const char *ZRESTRICT s);
24 | extern char *strerror(int errnum);
25 | extern int strerror_r(int errnum, char *strerrbuf, size_t buflen);
26 | extern char *strncpy(char *ZRESTRICT d, const char *ZRESTRICT s,
27 | size_t n);
28 | extern char *strchr(const char *s, int c);
29 | extern char *strrchr(const char *s, int c);
30 | extern size_t strlen(const char *s);
31 | extern size_t strnlen(const char *s, size_t maxlen);
32 | extern int strcmp(const char *s1, const char *s2);
33 | extern int strncmp(const char *s1, const char *s2, size_t n);
34 | extern char *strtok_r(char *str, const char *sep, char **state);
35 | extern char *strcat(char *ZRESTRICT dest,
36 | const char *ZRESTRICT src);
37 | extern char *strncat(char *ZRESTRICT dest, const char *ZRESTRICT src,
38 | size_t n);
39 | extern char *strstr(const char *s, const char *find);
40 |
41 | extern size_t strspn(const char *s, const char *accept);
42 | extern size_t strcspn(const char *s, const char *reject);
43 |
44 | extern int memcmp(const void *m1, const void *m2, size_t n);
45 | extern void *memmove(void *d, const void *s, size_t n);
46 | extern void *memcpy(void *ZRESTRICT d, const void *ZRESTRICT s,
47 | size_t n);
48 | extern void *memset(void *buf, int c, size_t n);
49 | extern void *memchr(const void *s, int c, size_t n);
50 |
51 | #ifdef __cplusplus
52 | }
53 | #endif
54 |
55 | #endif /* LIB_LIBC_MINIMAL_INCLUDE_STRING_H_ */
56 |
--------------------------------------------------------------------------------
/lib/libc/minimal/source/string/string.c:
--------------------------------------------------------------------------------
1 | /* string.c - common string routines */
2 |
3 | /*
4 | * Copyright (c) 2014 Wind River Systems, Inc.
5 | * Copyright (C) ASPEED Technology Inc.
6 | *
7 | * SPDX-License-Identifier: Apache-2.0
8 | */
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | /**
15 | *
16 | * @brief Copy a string
17 | *
18 | * @return pointer to destination buffer
19 | */
20 |
21 | char *strcpy(char *ZRESTRICT d, const char *ZRESTRICT s)
22 | {
23 | char *dest = d;
24 |
25 | while (*s != '\0') {
26 | *d = *s;
27 | d++;
28 | s++;
29 | }
30 |
31 | *d = '\0';
32 |
33 | return dest;
34 | }
35 |
36 | /**
37 | *
38 | * @brief Copy part of a string
39 | *
40 | * @return pointer to destination buffer
41 | */
42 |
43 | char *strncpy(char *ZRESTRICT d, const char *ZRESTRICT s, size_t n)
44 | {
45 | char *dest = d;
46 |
47 | while ((n > 0) && (*s != '\0')) {
48 | *d = *s;
49 | s++;
50 | d++;
51 | n--;
52 | }
53 |
54 | while (n > 0) {
55 | *d = '\0';
56 | d++;
57 | n--;
58 | }
59 |
60 | return dest;
61 | }
62 |
63 | /**
64 | *
65 | * @brief String scanning operation
66 | *
67 | * @return pointer to 1st instance of found byte, or NULL if not found
68 | */
69 |
70 | char *strchr(const char *s, int c)
71 | {
72 | char tmp = (char) c;
73 |
74 | while ((*s != tmp) && (*s != '\0')) {
75 | s++;
76 | }
77 |
78 | return (*s == tmp) ? (char *) s : NULL;
79 | }
80 |
81 | /**
82 | *
83 | * @brief String scanning operation
84 | *
85 | * @return pointer to last instance of found byte, or NULL if not found
86 | */
87 |
88 | char *strrchr(const char *s, int c)
89 | {
90 | char *match = NULL;
91 |
92 | do {
93 | if (*s == (char)c) {
94 | match = (char *)s;
95 | }
96 | } while (*s++ != '\0');
97 |
98 | return match;
99 | }
100 |
101 | /**
102 | * strlen - Find the length of a string
103 | * @s: The string to be sized
104 | */
105 | size_t strlen(const char *s)
106 | {
107 | const char *sc;
108 |
109 | for (sc = s; *sc != '\0'; ++sc)
110 | /* nothing */;
111 | return sc - s;
112 | }
113 |
114 | /**
115 | *
116 | * @brief Compare two strings
117 | *
118 | * @return negative # if < , 0 if == , else positive #
119 | */
120 |
121 | int strcmp(const char *s1, const char *s2)
122 | {
123 | while ((*s1 == *s2) && (*s1 != '\0')) {
124 | s1++;
125 | s2++;
126 | }
127 |
128 | return *s1 - *s2;
129 | }
130 |
131 | /**
132 | *
133 | * @brief Compare part of two strings
134 | *
135 | * @return negative # if < , 0 if == , else positive #
136 | */
137 |
138 | int strncmp(const char *s1, const char *s2, size_t n)
139 | {
140 | while ((n > 0) && (*s1 == *s2) && (*s1 != '\0')) {
141 | s1++;
142 | s2++;
143 | n--;
144 | }
145 |
146 | return (n == 0) ? 0 : (*s1 - *s2);
147 | }
148 |
149 | /**
150 | * @brief Separate `str` by any char in `sep` and return NULL terminated
151 | * sections. Consecutive `sep` chars in `str` are treated as a single
152 | * separator.
153 | *
154 | * @return pointer to NULL terminated string or NULL on errors.
155 | */
156 | char *strtok_r(char *str, const char *sep, char **state)
157 | {
158 | char *start, *end;
159 |
160 | start = str ? str : *state;
161 |
162 | /* skip leading delimiters */
163 | while (*start && strchr(sep, *start)) {
164 | start++;
165 | }
166 |
167 | if (*start == '\0') {
168 | *state = start;
169 | return NULL;
170 | }
171 |
172 | /* look for token chars */
173 | end = start;
174 | while (*end && !strchr(sep, *end)) {
175 | end++;
176 | }
177 |
178 | if (*end != '\0') {
179 | *end = '\0';
180 | *state = end + 1;
181 | } else {
182 | *state = end;
183 | }
184 |
185 | return start;
186 | }
187 |
188 | char *strcat(char *ZRESTRICT dest, const char *ZRESTRICT src)
189 | {
190 | strcpy(dest + strlen(dest), src);
191 | return dest;
192 | }
193 |
194 | char *strncat(char *ZRESTRICT dest, const char *ZRESTRICT src,
195 | size_t n)
196 | {
197 | char *orig_dest = dest;
198 | size_t len = strlen(dest);
199 |
200 | dest += len;
201 | while ((n-- > 0) && (*src != '\0')) {
202 | *dest++ = *src++;
203 | }
204 | *dest = '\0';
205 |
206 | return orig_dest;
207 | }
208 |
209 | /**
210 | *
211 | * @brief Compare two memory areas
212 | *
213 | * @return negative # if < , 0 if == , else positive #
214 | */
215 | int memcmp(const void *m1, const void *m2, size_t n)
216 | {
217 | const char *c1 = m1;
218 | const char *c2 = m2;
219 |
220 | if (!n) {
221 | return 0;
222 | }
223 |
224 | while ((--n > 0) && (*c1 == *c2)) {
225 | c1++;
226 | c2++;
227 | }
228 |
229 | return *c1 - *c2;
230 | }
231 |
232 | /**
233 | *
234 | * @brief Copy bytes in memory with overlapping areas
235 | *
236 | * @return pointer to destination buffer
237 | */
238 |
239 | void *memmove(void *d, const void *s, size_t n)
240 | {
241 | char *dest = d;
242 | const char *src = s;
243 |
244 | if ((size_t) (dest - src) < n) {
245 | /*
246 | * The buffer overlaps with the start of the buffer.
247 | * Copy backwards to prevent the premature corruption of .
248 | */
249 |
250 | while (n > 0) {
251 | n--;
252 | dest[n] = src[n];
253 | }
254 | } else {
255 | /* It is safe to perform a forward-copy */
256 | while (n > 0) {
257 | *dest = *src;
258 | dest++;
259 | src++;
260 | n--;
261 | }
262 | }
263 |
264 | return d;
265 | }
266 |
267 | /**
268 | *
269 | * @brief Copy bytes in memory
270 | *
271 | * @return pointer to start of destination buffer
272 | */
273 |
274 | void *memcpy(void *ZRESTRICT d, const void *ZRESTRICT s, size_t n)
275 | {
276 | /* attempt word-sized copying only if buffers have identical alignment */
277 | uintptr_t d_addr = (uintptr_t)d;
278 | uintptr_t s_addr = (uintptr_t)s;
279 |
280 | unsigned char *d_byte = (unsigned char *)(uintptr_t)d_addr;
281 | const unsigned char *s_byte = (const unsigned char *)(uintptr_t)s_addr;
282 |
283 | /* do byte-sized copying until finished */
284 |
285 | while (n > 0) {
286 | *(d_byte++) = *(s_byte++);
287 | n--;
288 | }
289 |
290 | return d;
291 | }
292 |
293 | /**
294 | *
295 | * @brief Set bytes in memory
296 | *
297 | * @return pointer to start of buffer
298 | */
299 |
300 | void *memset(void *buf, int c, size_t n)
301 | {
302 | /* do byte-sized initialization until word-aligned or finished */
303 |
304 | unsigned char *d_byte = (unsigned char *)buf;
305 | unsigned char c_byte = (unsigned char)c;
306 |
307 | while (n > 0) {
308 | *(d_byte++) = c_byte;
309 | n--;
310 | }
311 |
312 | return buf;
313 | }
314 |
315 | /**
316 | *
317 | * @brief Scan byte in memory
318 | *
319 | * @return pointer to start of found byte
320 | */
321 |
322 | void *memchr(const void *s, int c, size_t n)
323 | {
324 | if (n != 0) {
325 | const unsigned char *p = s;
326 |
327 | do {
328 | if (*p++ == (unsigned char)c) {
329 | return ((void *)(p - 1));
330 | }
331 |
332 | } while (--n != 0);
333 | }
334 |
335 | return NULL;
336 | }
337 |
338 | /**
339 | *
340 | * @brief Get fixed-size string length
341 | *
342 | * This function is not available in ARM C Standard library.
343 | *
344 | * @return number of bytes in fixed-size string
345 | */
346 |
347 | size_t strnlen(const char *s, size_t maxlen)
348 | {
349 | size_t n = 0;
350 |
351 | while (*s != '\0' && n < maxlen) {
352 | s++;
353 | n++;
354 | }
355 |
356 | return n;
357 | }
358 |
--------------------------------------------------------------------------------
/lib/libfdt/.gitignore:
--------------------------------------------------------------------------------
1 | libfdt.so.1
2 |
--------------------------------------------------------------------------------
/lib/libfdt/Makefile.libfdt:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | # Makefile.libfdt
3 | #
4 | # This is not a complete Makefile of itself. Instead, it is designed to
5 | # be easily embeddable into other systems of Makefiles.
6 | #
7 |
8 | LIBFDT_so = libfdt.$(SHAREDLIB_EXT)
9 | LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
10 | LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
11 | LIBFDT_VERSION = version.lds
12 | LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
13 | fdt_addresses.c fdt_overlay.c fdt_check.c
14 | LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
15 | LIBFDT_LIB = libfdt.$(SHAREDLIB_EXT).$(DTC_VERSION)
16 |
17 | libfdt_clean:
18 | @$(VECHO) CLEAN "(libfdt)"
19 | rm -f $(STD_CLEANFILES:%=$(LIBFDT_dir)/%)
20 | rm -f $(LIBFDT_dir)/$(LIBFDT_so)
21 | rm -f $(LIBFDT_dir)/$(LIBFDT_soname)
22 | rm -f $(LIBFDT_dir)/$(LIBFDT_LIB)
23 |
--------------------------------------------------------------------------------
/lib/libfdt/TODO:
--------------------------------------------------------------------------------
1 | - Tree traversal functions
2 | - Graft function
3 | - Complete libfdt.h documenting comments
4 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | /*
14 | * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
15 | * that the given buffer contains what appears to be a flattened
16 | * device tree with sane information in its header.
17 | */
18 | int32_t fdt_ro_probe_(const void *fdt)
19 | {
20 | uint32_t totalsize = fdt_totalsize(fdt);
21 |
22 | if (can_assume(VALID_DTB))
23 | return totalsize;
24 |
25 | /* The device tree must be at an 8-byte aligned address */
26 | if ((uintptr_t)fdt & 7)
27 | return -FDT_ERR_ALIGNMENT;
28 |
29 | if (fdt_magic(fdt) == FDT_MAGIC) {
30 | /* Complete tree */
31 | if (!can_assume(LATEST)) {
32 | if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
33 | return -FDT_ERR_BADVERSION;
34 | if (fdt_last_comp_version(fdt) >
35 | FDT_LAST_SUPPORTED_VERSION)
36 | return -FDT_ERR_BADVERSION;
37 | }
38 | } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
39 | /* Unfinished sequential-write blob */
40 | if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
41 | return -FDT_ERR_BADSTATE;
42 | } else {
43 | return -FDT_ERR_BADMAGIC;
44 | }
45 |
46 | if (totalsize < INT32_MAX)
47 | return totalsize;
48 | else
49 | return -FDT_ERR_TRUNCATED;
50 | }
51 |
52 | static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
53 | {
54 | return (off >= hdrsize) && (off <= totalsize);
55 | }
56 |
57 | static int check_block_(uint32_t hdrsize, uint32_t totalsize,
58 | uint32_t base, uint32_t size)
59 | {
60 | if (!check_off_(hdrsize, totalsize, base))
61 | return 0; /* block start out of bounds */
62 | if ((base + size) < base)
63 | return 0; /* overflow */
64 | if (!check_off_(hdrsize, totalsize, base + size))
65 | return 0; /* block end out of bounds */
66 | return 1;
67 | }
68 |
69 | size_t fdt_header_size_(uint32_t version)
70 | {
71 | if (version <= 1)
72 | return FDT_V1_SIZE;
73 | else if (version <= 2)
74 | return FDT_V2_SIZE;
75 | else if (version <= 3)
76 | return FDT_V3_SIZE;
77 | else if (version <= 16)
78 | return FDT_V16_SIZE;
79 | else
80 | return FDT_V17_SIZE;
81 | }
82 |
83 | size_t fdt_header_size(const void *fdt)
84 | {
85 | return can_assume(LATEST) ? FDT_V17_SIZE :
86 | fdt_header_size_(fdt_version(fdt));
87 | }
88 |
89 | int fdt_check_header(const void *fdt)
90 | {
91 | size_t hdrsize;
92 |
93 | /* The device tree must be at an 8-byte aligned address */
94 | if ((uintptr_t)fdt & 7)
95 | return -FDT_ERR_ALIGNMENT;
96 |
97 | if (fdt_magic(fdt) != FDT_MAGIC)
98 | return -FDT_ERR_BADMAGIC;
99 | if (!can_assume(LATEST)) {
100 | if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
101 | || (fdt_last_comp_version(fdt) >
102 | FDT_LAST_SUPPORTED_VERSION))
103 | return -FDT_ERR_BADVERSION;
104 | if (fdt_version(fdt) < fdt_last_comp_version(fdt))
105 | return -FDT_ERR_BADVERSION;
106 | }
107 | hdrsize = fdt_header_size(fdt);
108 | if (!can_assume(VALID_DTB)) {
109 | if ((fdt_totalsize(fdt) < hdrsize)
110 | || (fdt_totalsize(fdt) > INT_MAX))
111 | return -FDT_ERR_TRUNCATED;
112 |
113 | /* Bounds check memrsv block */
114 | if (!check_off_(hdrsize, fdt_totalsize(fdt),
115 | fdt_off_mem_rsvmap(fdt)))
116 | return -FDT_ERR_TRUNCATED;
117 |
118 | /* Bounds check structure block */
119 | if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
120 | if (!check_off_(hdrsize, fdt_totalsize(fdt),
121 | fdt_off_dt_struct(fdt)))
122 | return -FDT_ERR_TRUNCATED;
123 | } else {
124 | if (!check_block_(hdrsize, fdt_totalsize(fdt),
125 | fdt_off_dt_struct(fdt),
126 | fdt_size_dt_struct(fdt)))
127 | return -FDT_ERR_TRUNCATED;
128 | }
129 |
130 | /* Bounds check strings block */
131 | if (!check_block_(hdrsize, fdt_totalsize(fdt),
132 | fdt_off_dt_strings(fdt),
133 | fdt_size_dt_strings(fdt)))
134 | return -FDT_ERR_TRUNCATED;
135 | }
136 |
137 | return 0;
138 | }
139 |
140 | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
141 | {
142 | unsigned int uoffset = offset;
143 | unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
144 |
145 | if (offset < 0)
146 | return NULL;
147 |
148 | if (!can_assume(VALID_INPUT))
149 | if ((absoffset < uoffset)
150 | || ((absoffset + len) < absoffset)
151 | || (absoffset + len) > fdt_totalsize(fdt))
152 | return NULL;
153 |
154 | if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
155 | if (((uoffset + len) < uoffset)
156 | || ((offset + len) > fdt_size_dt_struct(fdt)))
157 | return NULL;
158 |
159 | return fdt_offset_ptr_(fdt, offset);
160 | }
161 |
162 | uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
163 | {
164 | const fdt32_t *tagp, *lenp;
165 | uint32_t tag, len, sum;
166 | int offset = startoffset;
167 | const char *p;
168 |
169 | *nextoffset = -FDT_ERR_TRUNCATED;
170 | tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
171 | if (!can_assume(VALID_DTB) && !tagp)
172 | return FDT_END; /* premature end */
173 | tag = fdt32_to_cpu(*tagp);
174 | offset += FDT_TAGSIZE;
175 |
176 | *nextoffset = -FDT_ERR_BADSTRUCTURE;
177 | switch (tag) {
178 | case FDT_BEGIN_NODE:
179 | /* skip name */
180 | do {
181 | p = fdt_offset_ptr(fdt, offset++, 1);
182 | } while (p && (*p != '\0'));
183 | if (!can_assume(VALID_DTB) && !p)
184 | return FDT_END; /* premature end */
185 | break;
186 |
187 | case FDT_PROP:
188 | lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
189 | if (!can_assume(VALID_DTB) && !lenp)
190 | return FDT_END; /* premature end */
191 |
192 | len = fdt32_to_cpu(*lenp);
193 | sum = len + offset;
194 | if (!can_assume(VALID_DTB) &&
195 | (INT_MAX <= sum || sum < (uint32_t) offset))
196 | return FDT_END; /* premature end */
197 |
198 | /* skip-name offset, length and value */
199 | offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;
200 |
201 | if (!can_assume(LATEST) &&
202 | fdt_version(fdt) < 0x10 && len >= 8 &&
203 | ((offset - len) % 8) != 0)
204 | offset += 4;
205 | break;
206 |
207 | case FDT_END:
208 | case FDT_END_NODE:
209 | case FDT_NOP:
210 | break;
211 |
212 | default:
213 | return FDT_END;
214 | }
215 |
216 | if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
217 | return FDT_END; /* premature end */
218 |
219 | *nextoffset = FDT_TAGALIGN(offset);
220 | return tag;
221 | }
222 |
223 | int fdt_check_node_offset_(const void *fdt, int offset)
224 | {
225 | if (!can_assume(VALID_INPUT)
226 | && ((offset < 0) || (offset % FDT_TAGSIZE)))
227 | return -FDT_ERR_BADOFFSET;
228 |
229 | if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
230 | return -FDT_ERR_BADOFFSET;
231 |
232 | return offset;
233 | }
234 |
235 | int fdt_check_prop_offset_(const void *fdt, int offset)
236 | {
237 | if (!can_assume(VALID_INPUT)
238 | && ((offset < 0) || (offset % FDT_TAGSIZE)))
239 | return -FDT_ERR_BADOFFSET;
240 |
241 | if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
242 | return -FDT_ERR_BADOFFSET;
243 |
244 | return offset;
245 | }
246 |
247 | int fdt_next_node(const void *fdt, int offset, int *depth)
248 | {
249 | int nextoffset = 0;
250 | uint32_t tag;
251 |
252 | if (offset >= 0)
253 | if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
254 | return nextoffset;
255 |
256 | do {
257 | offset = nextoffset;
258 | tag = fdt_next_tag(fdt, offset, &nextoffset);
259 |
260 | switch (tag) {
261 | case FDT_PROP:
262 | case FDT_NOP:
263 | break;
264 |
265 | case FDT_BEGIN_NODE:
266 | if (depth)
267 | (*depth)++;
268 | break;
269 |
270 | case FDT_END_NODE:
271 | if (depth && ((--(*depth)) < 0))
272 | return nextoffset;
273 | break;
274 |
275 | case FDT_END:
276 | if ((nextoffset >= 0)
277 | || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
278 | return -FDT_ERR_NOTFOUND;
279 | else
280 | return nextoffset;
281 | }
282 | } while (tag != FDT_BEGIN_NODE);
283 |
284 | return offset;
285 | }
286 |
287 | int fdt_first_subnode(const void *fdt, int offset)
288 | {
289 | int depth = 0;
290 |
291 | offset = fdt_next_node(fdt, offset, &depth);
292 | if (offset < 0 || depth != 1)
293 | return -FDT_ERR_NOTFOUND;
294 |
295 | return offset;
296 | }
297 |
298 | int fdt_next_subnode(const void *fdt, int offset)
299 | {
300 | int depth = 1;
301 |
302 | /*
303 | * With respect to the parent, the depth of the next subnode will be
304 | * the same as the last.
305 | */
306 | do {
307 | offset = fdt_next_node(fdt, offset, &depth);
308 | if (offset < 0 || depth < 1)
309 | return -FDT_ERR_NOTFOUND;
310 | } while (depth > 1);
311 |
312 | return offset;
313 | }
314 |
315 | const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
316 | int slen)
317 | {
318 | const char *last = strtab + tabsize - (slen + 1);
319 | const char *p;
320 |
321 | for (p = strtab; p <= last; p++)
322 | if (memcmp(p, s, slen) == 0 && p[slen] == '\0')
323 | return p;
324 | return NULL;
325 | }
326 |
327 | int fdt_move(const void *fdt, void *buf, int bufsize)
328 | {
329 | if (!can_assume(VALID_INPUT) && bufsize < 0)
330 | return -FDT_ERR_NOSPACE;
331 |
332 | FDT_RO_PROBE(fdt);
333 |
334 | if (fdt_totalsize(fdt) > (unsigned int)bufsize)
335 | return -FDT_ERR_NOSPACE;
336 |
337 | memmove(buf, fdt, fdt_totalsize(fdt));
338 | return 0;
339 | }
340 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
2 | #ifndef FDT_H
3 | #define FDT_H
4 | /*
5 | * libfdt - Flat Device Tree manipulation
6 | * Copyright (C) 2006 David Gibson, IBM Corporation.
7 | * Copyright 2012 Kim Phillips, Freescale Semiconductor.
8 | */
9 |
10 | #ifndef __ASSEMBLER__
11 |
12 | struct fdt_header {
13 | fdt32_t magic; /* magic word FDT_MAGIC */
14 | fdt32_t totalsize; /* total size of DT block */
15 | fdt32_t off_dt_struct; /* offset to structure */
16 | fdt32_t off_dt_strings; /* offset to strings */
17 | fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
18 | fdt32_t version; /* format version */
19 | fdt32_t last_comp_version; /* last compatible version */
20 |
21 | /* version 2 fields below */
22 | fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
23 | booting on */
24 | /* version 3 fields below */
25 | fdt32_t size_dt_strings; /* size of the strings block */
26 |
27 | /* version 17 fields below */
28 | fdt32_t size_dt_struct; /* size of the structure block */
29 | };
30 |
31 | struct fdt_reserve_entry {
32 | fdt64_t address;
33 | fdt64_t size;
34 | };
35 |
36 | struct fdt_node_header {
37 | fdt32_t tag;
38 | char name[];
39 | };
40 |
41 | struct fdt_property {
42 | fdt32_t tag;
43 | fdt32_t len;
44 | fdt32_t nameoff;
45 | char data[];
46 | };
47 |
48 | #endif /* !__ASSEMBLER__ */
49 |
50 | #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
51 | #define FDT_TAGSIZE sizeof(fdt32_t)
52 |
53 | #define FDT_BEGIN_NODE 0x1 /* Start node: full name */
54 | #define FDT_END_NODE 0x2 /* End node */
55 | #define FDT_PROP 0x3 /* Property: name off,
56 | size, content */
57 | #define FDT_NOP 0x4 /* nop */
58 | #define FDT_END 0x9
59 |
60 | #define FDT_V1_SIZE (7*sizeof(fdt32_t))
61 | #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
62 | #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
63 | #define FDT_V16_SIZE FDT_V3_SIZE
64 | #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
65 |
66 | #endif /* FDT_H */
67 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_addresses.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2014 David Gibson
5 | * Copyright (C) 2018 embedded brains GmbH
6 | */
7 | #include "libfdt_env.h"
8 |
9 | #include
10 | #include
11 |
12 | #include "libfdt_internal.h"
13 |
14 | static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
15 | {
16 | const fdt32_t *c;
17 | uint32_t val;
18 | int len;
19 |
20 | c = fdt_getprop(fdt, nodeoffset, name, &len);
21 | if (!c)
22 | return len;
23 |
24 | if (len != sizeof(*c))
25 | return -FDT_ERR_BADNCELLS;
26 |
27 | val = fdt32_to_cpu(*c);
28 | if (val > FDT_MAX_NCELLS)
29 | return -FDT_ERR_BADNCELLS;
30 |
31 | return (int)val;
32 | }
33 |
34 | int fdt_address_cells(const void *fdt, int nodeoffset)
35 | {
36 | int val;
37 |
38 | val = fdt_cells(fdt, nodeoffset, "#address-cells");
39 | if (val == 0)
40 | return -FDT_ERR_BADNCELLS;
41 | if (val == -FDT_ERR_NOTFOUND)
42 | return 2;
43 | return val;
44 | }
45 |
46 | int fdt_size_cells(const void *fdt, int nodeoffset)
47 | {
48 | int val;
49 |
50 | val = fdt_cells(fdt, nodeoffset, "#size-cells");
51 | if (val == -FDT_ERR_NOTFOUND)
52 | return 1;
53 | return val;
54 | }
55 |
56 | /* This function assumes that [address|size]_cells is 1 or 2 */
57 | int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
58 | const char *name, uint64_t addr, uint64_t size)
59 | {
60 | int addr_cells, size_cells, ret;
61 | uint8_t data[sizeof(fdt64_t) * 2], *prop;
62 |
63 | ret = fdt_address_cells(fdt, parent);
64 | if (ret < 0)
65 | return ret;
66 | addr_cells = ret;
67 |
68 | ret = fdt_size_cells(fdt, parent);
69 | if (ret < 0)
70 | return ret;
71 | size_cells = ret;
72 |
73 | /* check validity of address */
74 | prop = data;
75 | if (addr_cells == 1) {
76 | if ((addr > UINT32_MAX) || (((uint64_t) UINT32_MAX + 1 - addr) < size))
77 | return -FDT_ERR_BADVALUE;
78 |
79 | fdt32_st(prop, (uint32_t)addr);
80 | } else if (addr_cells == 2) {
81 | fdt64_st(prop, addr);
82 | } else {
83 | return -FDT_ERR_BADNCELLS;
84 | }
85 |
86 | /* check validity of size */
87 | prop += addr_cells * sizeof(fdt32_t);
88 | if (size_cells == 1) {
89 | if (size > UINT32_MAX)
90 | return -FDT_ERR_BADVALUE;
91 |
92 | fdt32_st(prop, (uint32_t)size);
93 | } else if (size_cells == 2) {
94 | fdt64_st(prop, size);
95 | } else {
96 | return -FDT_ERR_BADNCELLS;
97 | }
98 |
99 | return fdt_appendprop(fdt, nodeoffset, name, data,
100 | (addr_cells + size_cells) * sizeof(fdt32_t));
101 | }
102 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_check.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | int fdt_check_full(const void *fdt, size_t bufsize)
14 | {
15 | int err;
16 | int num_memrsv;
17 | int offset, nextoffset = 0;
18 | uint32_t tag;
19 | unsigned int depth = 0;
20 | const void *prop;
21 | const char *propname;
22 | bool expect_end = false;
23 |
24 | if (bufsize < FDT_V1_SIZE)
25 | return -FDT_ERR_TRUNCATED;
26 | if (bufsize < fdt_header_size(fdt))
27 | return -FDT_ERR_TRUNCATED;
28 | err = fdt_check_header(fdt);
29 | if (err != 0)
30 | return err;
31 | if (bufsize < fdt_totalsize(fdt))
32 | return -FDT_ERR_TRUNCATED;
33 |
34 | num_memrsv = fdt_num_mem_rsv(fdt);
35 | if (num_memrsv < 0)
36 | return num_memrsv;
37 |
38 | while (1) {
39 | offset = nextoffset;
40 | tag = fdt_next_tag(fdt, offset, &nextoffset);
41 |
42 | if (nextoffset < 0)
43 | return nextoffset;
44 |
45 | /* If we see two root nodes, something is wrong */
46 | if (expect_end && tag != FDT_END)
47 | return -FDT_ERR_BADSTRUCTURE;
48 |
49 | switch (tag) {
50 | case FDT_NOP:
51 | break;
52 |
53 | case FDT_END:
54 | if (depth != 0)
55 | return -FDT_ERR_BADSTRUCTURE;
56 | return 0;
57 |
58 | case FDT_BEGIN_NODE:
59 | depth++;
60 | if (depth > INT_MAX)
61 | return -FDT_ERR_BADSTRUCTURE;
62 |
63 | /* The root node must have an empty name */
64 | if (depth == 1) {
65 | const char *name;
66 | int len;
67 |
68 | name = fdt_get_name(fdt, offset, &len);
69 | if (!name)
70 | return len;
71 |
72 | if (*name || len)
73 | return -FDT_ERR_BADSTRUCTURE;
74 | }
75 | break;
76 |
77 | case FDT_END_NODE:
78 | if (depth == 0)
79 | return -FDT_ERR_BADSTRUCTURE;
80 | depth--;
81 | if (depth == 0)
82 | expect_end = true;
83 | break;
84 |
85 | case FDT_PROP:
86 | prop = fdt_getprop_by_offset(fdt, offset, &propname,
87 | &err);
88 | if (!prop)
89 | return err;
90 | break;
91 |
92 | default:
93 | return -FDT_ERR_INTERNAL;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_empty_tree.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2012 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | int fdt_create_empty_tree(void *buf, int bufsize)
14 | {
15 | int err;
16 |
17 | err = fdt_create(buf, bufsize);
18 | if (err)
19 | return err;
20 |
21 | err = fdt_finish_reservemap(buf);
22 | if (err)
23 | return err;
24 |
25 | err = fdt_begin_node(buf, "");
26 | if (err)
27 | return err;
28 |
29 | err = fdt_end_node(buf);
30 | if (err)
31 | return err;
32 |
33 | err = fdt_finish(buf);
34 | if (err)
35 | return err;
36 |
37 | return fdt_open_into(buf, buf, bufsize);
38 | }
39 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_overlay.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2016 Free Electrons
5 | * Copyright (C) 2016 NextThing Co.
6 | */
7 | #include "libfdt_env.h"
8 |
9 | #include
10 | #include
11 |
12 | #include "libfdt_internal.h"
13 |
14 | /**
15 | * overlay_get_target_phandle - retrieves the target phandle of a fragment
16 | * @fdto: pointer to the device tree overlay blob
17 | * @fragment: node offset of the fragment in the overlay
18 | *
19 | * overlay_get_target_phandle() retrieves the target phandle of an
20 | * overlay fragment when that fragment uses a phandle (target
21 | * property) instead of a path (target-path property).
22 | *
23 | * returns:
24 | * the phandle pointed by the target property
25 | * 0, if the phandle was not found
26 | * -1, if the phandle was malformed
27 | */
28 | static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
29 | {
30 | const fdt32_t *val;
31 | int len;
32 |
33 | val = fdt_getprop(fdto, fragment, "target", &len);
34 | if (!val)
35 | return 0;
36 |
37 | if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
38 | return (uint32_t)-1;
39 |
40 | return fdt32_to_cpu(*val);
41 | }
42 |
43 | int fdt_overlay_target_offset(const void *fdt, const void *fdto,
44 | int fragment_offset, char const **pathp)
45 | {
46 | uint32_t phandle;
47 | const char *path = NULL;
48 | int path_len = 0, ret;
49 |
50 | /* Try first to do a phandle based lookup */
51 | phandle = overlay_get_target_phandle(fdto, fragment_offset);
52 | if (phandle == (uint32_t)-1)
53 | return -FDT_ERR_BADPHANDLE;
54 |
55 | /* no phandle, try path */
56 | if (!phandle) {
57 | /* And then a path based lookup */
58 | path = fdt_getprop(fdto, fragment_offset, "target-path", &path_len);
59 | if (path)
60 | ret = fdt_path_offset(fdt, path);
61 | else
62 | ret = path_len;
63 | } else
64 | ret = fdt_node_offset_by_phandle(fdt, phandle);
65 |
66 | /*
67 | * If we haven't found either a target or a
68 | * target-path property in a node that contains a
69 | * __overlay__ subnode (we wouldn't be called
70 | * otherwise), consider it a improperly written
71 | * overlay
72 | */
73 | if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
74 | ret = -FDT_ERR_BADOVERLAY;
75 |
76 | /* return on error */
77 | if (ret < 0)
78 | return ret;
79 |
80 | /* return pointer to path (if available) */
81 | if (pathp)
82 | *pathp = path ? path : NULL;
83 |
84 | return ret;
85 | }
86 |
87 | /**
88 | * overlay_phandle_add_offset - Increases a phandle by an offset
89 | * @fdt: Base device tree blob
90 | * @node: Device tree overlay blob
91 | * @name: Name of the property to modify (phandle or linux,phandle)
92 | * @delta: offset to apply
93 | *
94 | * overlay_phandle_add_offset() increments a node phandle by a given
95 | * offset.
96 | *
97 | * returns:
98 | * 0 on success.
99 | * Negative error code on error
100 | */
101 | static int overlay_phandle_add_offset(void *fdt, int node,
102 | const char *name, uint32_t delta)
103 | {
104 | fdt32_t *valp, val;
105 | int len;
106 |
107 | valp = fdt_getprop_w(fdt, node, name, &len);
108 | if (!valp)
109 | return len;
110 |
111 | if (len != sizeof(val))
112 | return -FDT_ERR_BADPHANDLE;
113 |
114 | val = fdt32_ld(valp);
115 | if (val + delta < val || val + delta == (uint32_t)-1)
116 | return -FDT_ERR_NOPHANDLES;
117 |
118 | fdt32_st(valp, val + delta);
119 | return 0;
120 | }
121 |
122 | /**
123 | * overlay_adjust_node_phandles - Offsets the phandles of a node
124 | * @fdto: Device tree overlay blob
125 | * @node: Offset of the node we want to adjust
126 | * @delta: Offset to shift the phandles of
127 | *
128 | * overlay_adjust_node_phandles() adds a constant to all the phandles
129 | * of a given node. This is mainly use as part of the overlay
130 | * application process, when we want to update all the overlay
131 | * phandles to not conflict with the overlays of the base device tree.
132 | *
133 | * returns:
134 | * 0 on success
135 | * Negative error code on failure
136 | */
137 | static int overlay_adjust_node_phandles(void *fdto, int node,
138 | uint32_t delta)
139 | {
140 | int child;
141 | int ret;
142 |
143 | ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
144 | if (ret && ret != -FDT_ERR_NOTFOUND)
145 | return ret;
146 |
147 | ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
148 | if (ret && ret != -FDT_ERR_NOTFOUND)
149 | return ret;
150 |
151 | fdt_for_each_subnode(child, fdto, node) {
152 | ret = overlay_adjust_node_phandles(fdto, child, delta);
153 | if (ret)
154 | return ret;
155 | }
156 |
157 | return 0;
158 | }
159 |
160 | /**
161 | * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
162 | * @fdto: Device tree overlay blob
163 | * @delta: Offset to shift the phandles of
164 | *
165 | * overlay_adjust_local_phandles() adds a constant to all the
166 | * phandles of an overlay. This is mainly use as part of the overlay
167 | * application process, when we want to update all the overlay
168 | * phandles to not conflict with the overlays of the base device tree.
169 | *
170 | * returns:
171 | * 0 on success
172 | * Negative error code on failure
173 | */
174 | static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
175 | {
176 | /*
177 | * Start adjusting the phandles from the overlay root
178 | */
179 | return overlay_adjust_node_phandles(fdto, 0, delta);
180 | }
181 |
182 | /**
183 | * overlay_update_local_node_references - Adjust the overlay references
184 | * @fdto: Device tree overlay blob
185 | * @tree_node: Node offset of the node to operate on
186 | * @fixup_node: Node offset of the matching local fixups node
187 | * @delta: Offset to shift the phandles of
188 | *
189 | * overlay_update_local_nodes_references() update the phandles
190 | * pointing to a node within the device tree overlay by adding a
191 | * constant delta.
192 | *
193 | * This is mainly used as part of a device tree application process,
194 | * where you want the device tree overlays phandles to not conflict
195 | * with the ones from the base device tree before merging them.
196 | *
197 | * returns:
198 | * 0 on success
199 | * Negative error code on failure
200 | */
201 | static int overlay_update_local_node_references(void *fdto,
202 | int tree_node,
203 | int fixup_node,
204 | uint32_t delta)
205 | {
206 | int fixup_prop;
207 | int fixup_child;
208 | int ret;
209 |
210 | fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
211 | const fdt32_t *fixup_val;
212 | const char *name;
213 | char *tree_val;
214 | int fixup_len;
215 | int tree_len;
216 | int i;
217 |
218 | fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
219 | &name, &fixup_len);
220 | if (!fixup_val)
221 | return fixup_len;
222 |
223 | if (fixup_len % sizeof(uint32_t))
224 | return -FDT_ERR_BADOVERLAY;
225 | fixup_len /= sizeof(uint32_t);
226 |
227 | tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
228 | if (!tree_val) {
229 | if (tree_len == -FDT_ERR_NOTFOUND)
230 | return -FDT_ERR_BADOVERLAY;
231 |
232 | return tree_len;
233 | }
234 |
235 | for (i = 0; i < fixup_len; i++) {
236 | fdt32_t *refp;
237 |
238 | refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
239 |
240 | /*
241 | * phandles to fixup can be unaligned, so use
242 | * fdt32_{ld,st}() to read/write them.
243 | */
244 | fdt32_st(refp, fdt32_ld(refp) + delta);
245 | }
246 | }
247 |
248 | fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
249 | const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
250 | NULL);
251 | int tree_child;
252 |
253 | tree_child = fdt_subnode_offset(fdto, tree_node,
254 | fixup_child_name);
255 | if (tree_child == -FDT_ERR_NOTFOUND)
256 | return -FDT_ERR_BADOVERLAY;
257 | if (tree_child < 0)
258 | return tree_child;
259 |
260 | ret = overlay_update_local_node_references(fdto,
261 | tree_child,
262 | fixup_child,
263 | delta);
264 | if (ret)
265 | return ret;
266 | }
267 |
268 | return 0;
269 | }
270 |
271 | /**
272 | * overlay_update_local_references - Adjust the overlay references
273 | * @fdto: Device tree overlay blob
274 | * @delta: Offset to shift the phandles of
275 | *
276 | * overlay_update_local_references() update all the phandles pointing
277 | * to a node within the device tree overlay by adding a constant
278 | * delta to not conflict with the base overlay.
279 | *
280 | * This is mainly used as part of a device tree application process,
281 | * where you want the device tree overlays phandles to not conflict
282 | * with the ones from the base device tree before merging them.
283 | *
284 | * returns:
285 | * 0 on success
286 | * Negative error code on failure
287 | */
288 | static int overlay_update_local_references(void *fdto, uint32_t delta)
289 | {
290 | int fixups;
291 |
292 | fixups = fdt_path_offset(fdto, "/__local_fixups__");
293 | if (fixups < 0) {
294 | /* There's no local phandles to adjust, bail out */
295 | if (fixups == -FDT_ERR_NOTFOUND)
296 | return 0;
297 |
298 | return fixups;
299 | }
300 |
301 | /*
302 | * Update our local references from the root of the tree
303 | */
304 | return overlay_update_local_node_references(fdto, 0, fixups,
305 | delta);
306 | }
307 |
308 | /**
309 | * overlay_fixup_one_phandle - Set an overlay phandle to the base one
310 | * @fdto: Device tree overlay blob
311 | * @symbols_off: Node offset of the symbols node in the base device tree
312 | * @path: Path to a node holding a phandle in the overlay
313 | * @path_len: number of path characters to consider
314 | * @name: Name of the property holding the phandle reference in the overlay
315 | * @name_len: number of name characters to consider
316 | * @poffset: Offset within the overlay property where the phandle is stored
317 | * @phandle: Phandle referencing the node
318 | *
319 | * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
320 | * a node in the base device tree.
321 | *
322 | * This is part of the device tree overlay application process, when
323 | * you want all the phandles in the overlay to point to the actual
324 | * base dt nodes.
325 | *
326 | * returns:
327 | * 0 on success
328 | * Negative error code on failure
329 | */
330 | static int overlay_fixup_one_phandle(void *fdto, int symbols_off,
331 | const char *path, uint32_t path_len,
332 | const char *name, uint32_t name_len,
333 | int poffset, uint32_t phandle)
334 | {
335 | fdt32_t phandle_prop;
336 | int fixup_off;
337 |
338 | if (symbols_off < 0)
339 | return symbols_off;
340 |
341 | fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
342 | if (fixup_off == -FDT_ERR_NOTFOUND)
343 | return -FDT_ERR_BADOVERLAY;
344 | if (fixup_off < 0)
345 | return fixup_off;
346 |
347 | phandle_prop = cpu_to_fdt32(phandle);
348 | return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
349 | name, name_len, poffset,
350 | &phandle_prop,
351 | sizeof(phandle_prop));
352 | }
353 |
354 | /**
355 | * overlay_fixup_phandle - Set an overlay phandle to the base one
356 | * @fdt: Base Device Tree blob
357 | * @fdto: Device tree overlay blob
358 | * @symbols_off: Node offset of the symbols node in the base device tree
359 | * @property: Property offset in the overlay holding the list of fixups
360 | *
361 | * overlay_fixup_phandle() resolves all the overlay phandles pointed
362 | * to in a __fixups__ property, and updates them to match the phandles
363 | * in use in the base device tree.
364 | *
365 | * This is part of the device tree overlay application process, when
366 | * you want all the phandles in the overlay to point to the actual
367 | * base dt nodes.
368 | *
369 | * returns:
370 | * 0 on success
371 | * Negative error code on failure
372 | */
373 | static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
374 | int property)
375 | {
376 | const char *value;
377 | const char *label;
378 | int len;
379 | const char *symbol_path;
380 | int prop_len;
381 | int symbol_off;
382 | uint32_t phandle;
383 |
384 | value = fdt_getprop_by_offset(fdto, property,
385 | &label, &len);
386 | if (!value) {
387 | if (len == -FDT_ERR_NOTFOUND)
388 | return -FDT_ERR_INTERNAL;
389 |
390 | return len;
391 | }
392 |
393 | symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len);
394 | if (!symbol_path)
395 | return prop_len;
396 |
397 | symbol_off = fdt_path_offset(fdt, symbol_path);
398 | if (symbol_off < 0)
399 | return symbol_off;
400 |
401 | phandle = fdt_get_phandle(fdt, symbol_off);
402 | if (!phandle)
403 | return -FDT_ERR_NOTFOUND;
404 |
405 | do {
406 | const char *path, *name, *fixup_end;
407 | const char *fixup_str = value;
408 | uint32_t path_len, name_len;
409 | uint32_t fixup_len;
410 | char *sep, *endptr;
411 | int poffset, ret;
412 |
413 | fixup_end = memchr(value, '\0', len);
414 | if (!fixup_end)
415 | return -FDT_ERR_BADOVERLAY;
416 | fixup_len = fixup_end - fixup_str;
417 |
418 | len -= fixup_len + 1;
419 | value += fixup_len + 1;
420 |
421 | path = fixup_str;
422 | sep = memchr(fixup_str, ':', fixup_len);
423 | if (!sep || *sep != ':')
424 | return -FDT_ERR_BADOVERLAY;
425 |
426 | path_len = sep - path;
427 | if (path_len == (fixup_len - 1))
428 | return -FDT_ERR_BADOVERLAY;
429 |
430 | fixup_len -= path_len + 1;
431 | name = sep + 1;
432 | sep = memchr(name, ':', fixup_len);
433 | if (!sep || *sep != ':')
434 | return -FDT_ERR_BADOVERLAY;
435 |
436 | name_len = sep - name;
437 | if (!name_len)
438 | return -FDT_ERR_BADOVERLAY;
439 |
440 | poffset = strtoul(sep + 1, &endptr, 10);
441 | if ((*endptr != '\0') || (endptr <= (sep + 1)))
442 | return -FDT_ERR_BADOVERLAY;
443 |
444 | ret = overlay_fixup_one_phandle(fdto, symbols_off,
445 | path, path_len, name, name_len,
446 | poffset, phandle);
447 | if (ret)
448 | return ret;
449 | } while (len > 0);
450 |
451 | return 0;
452 | }
453 |
454 | /**
455 | * overlay_fixup_phandles - Resolve the overlay phandles to the base
456 | * device tree
457 | * @fdt: Base Device Tree blob
458 | * @fdto: Device tree overlay blob
459 | *
460 | * overlay_fixup_phandles() resolves all the overlay phandles pointing
461 | * to nodes in the base device tree.
462 | *
463 | * This is one of the steps of the device tree overlay application
464 | * process, when you want all the phandles in the overlay to point to
465 | * the actual base dt nodes.
466 | *
467 | * returns:
468 | * 0 on success
469 | * Negative error code on failure
470 | */
471 | static int overlay_fixup_phandles(void *fdt, void *fdto)
472 | {
473 | int fixups_off, symbols_off;
474 | int property;
475 |
476 | /* We can have overlays without any fixups */
477 | fixups_off = fdt_path_offset(fdto, "/__fixups__");
478 | if (fixups_off == -FDT_ERR_NOTFOUND)
479 | return 0; /* nothing to do */
480 | if (fixups_off < 0)
481 | return fixups_off;
482 |
483 | /* And base DTs without symbols */
484 | symbols_off = fdt_path_offset(fdt, "/__symbols__");
485 | if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
486 | return symbols_off;
487 |
488 | fdt_for_each_property_offset(property, fdto, fixups_off) {
489 | int ret;
490 |
491 | ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
492 | if (ret)
493 | return ret;
494 | }
495 |
496 | return 0;
497 | }
498 |
499 | /**
500 | * overlay_adjust_local_conflicting_phandle: Changes a phandle value
501 | * @fdto: Device tree overlay
502 | * @node: The node the phandle is set for
503 | * @fdt_phandle: The new value for the phandle
504 | *
505 | * returns:
506 | * 0 on success
507 | * Negative error code on failure
508 | */
509 | static int overlay_adjust_local_conflicting_phandle(void *fdto, int node,
510 | uint32_t fdt_phandle)
511 | {
512 | const fdt32_t *php;
513 | int len, ret;
514 |
515 | php = fdt_getprop(fdto, node, "phandle", &len);
516 | if (php && len == sizeof(*php)) {
517 | ret = fdt_setprop_inplace_u32(fdto, node, "phandle", fdt_phandle);
518 | if (ret)
519 | return ret;
520 | }
521 |
522 | php = fdt_getprop(fdto, node, "linux,phandle", &len);
523 | if (php && len == sizeof(*php)) {
524 | ret = fdt_setprop_inplace_u32(fdto, node, "linux,phandle", fdt_phandle);
525 | if (ret)
526 | return ret;
527 | }
528 |
529 | return 0;
530 | }
531 |
532 | /**
533 | * overlay_update_node_conflicting_references - Recursively replace phandle values
534 | * @fdto: Device tree overlay blob
535 | * @tree_node: Node to recurse into
536 | * @fixup_node: Node offset of the matching local fixups node
537 | * @fdt_phandle: Value to replace phandles with
538 | * @fdto_phandle: Value to be replaced
539 | *
540 | * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
541 | *
542 | * returns:
543 | * 0 on success
544 | * Negative error code on failure
545 | */
546 | static int overlay_update_node_conflicting_references(void *fdto, int tree_node,
547 | int fixup_node,
548 | uint32_t fdt_phandle,
549 | uint32_t fdto_phandle)
550 | {
551 | int fixup_prop;
552 | int fixup_child;
553 | int ret;
554 |
555 | fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
556 | const fdt32_t *fixup_val;
557 | const char *name;
558 | char *tree_val;
559 | int fixup_len;
560 | int tree_len;
561 | int i;
562 |
563 | fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
564 | &name, &fixup_len);
565 | if (!fixup_val)
566 | return fixup_len;
567 |
568 | if (fixup_len % sizeof(uint32_t))
569 | return -FDT_ERR_BADOVERLAY;
570 | fixup_len /= sizeof(uint32_t);
571 |
572 | tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
573 | if (!tree_val) {
574 | if (tree_len == -FDT_ERR_NOTFOUND)
575 | return -FDT_ERR_BADOVERLAY;
576 |
577 | return tree_len;
578 | }
579 |
580 | for (i = 0; i < fixup_len; i++) {
581 | fdt32_t *refp;
582 | uint32_t valp;
583 |
584 | refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
585 | valp = fdt32_ld(refp);
586 |
587 | if (valp == fdto_phandle)
588 | fdt32_st(refp, fdt_phandle);
589 | }
590 | }
591 |
592 | fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
593 | const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL);
594 | int tree_child;
595 |
596 | tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name);
597 |
598 | if (tree_child == -FDT_ERR_NOTFOUND)
599 | return -FDT_ERR_BADOVERLAY;
600 | if (tree_child < 0)
601 | return tree_child;
602 |
603 | ret = overlay_update_node_conflicting_references(fdto, tree_child,
604 | fixup_child,
605 | fdt_phandle,
606 | fdto_phandle);
607 | if (ret)
608 | return ret;
609 | }
610 |
611 | return 0;
612 | }
613 |
614 | /**
615 | * overlay_update_local_conflicting_references - Recursively replace phandle values
616 | * @fdto: Device tree overlay blob
617 | * @fdt_phandle: Value to replace phandles with
618 | * @fdto_phandle: Value to be replaced
619 | *
620 | * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
621 | *
622 | * returns:
623 | * 0 on success
624 | * Negative error code on failure
625 | */
626 | static int overlay_update_local_conflicting_references(void *fdto,
627 | uint32_t fdt_phandle,
628 | uint32_t fdto_phandle)
629 | {
630 | int fixups;
631 |
632 | fixups = fdt_path_offset(fdto, "/__local_fixups__");
633 | if (fixups == -FDT_ERR_NOTFOUND)
634 | return 0;
635 | if (fixups < 0)
636 | return fixups;
637 |
638 | return overlay_update_node_conflicting_references(fdto, 0, fixups,
639 | fdt_phandle,
640 | fdto_phandle);
641 | }
642 |
643 | /**
644 | * overlay_prevent_phandle_overwrite_node - Helper function for overlay_prevent_phandle_overwrite
645 | * @fdt: Base Device tree blob
646 | * @fdtnode: Node in fdt that is checked for an overwrite
647 | * @fdto: Device tree overlay blob
648 | * @fdtonode: Node in fdto matching @fdtnode
649 | *
650 | * returns:
651 | * 0 on success
652 | * Negative error code on failure
653 | */
654 | static int overlay_prevent_phandle_overwrite_node(void *fdt, int fdtnode,
655 | void *fdto, int fdtonode)
656 | {
657 | uint32_t fdt_phandle, fdto_phandle;
658 | int fdtochild;
659 |
660 | fdt_phandle = fdt_get_phandle(fdt, fdtnode);
661 | fdto_phandle = fdt_get_phandle(fdto, fdtonode);
662 |
663 | if (fdt_phandle && fdto_phandle) {
664 | int ret;
665 |
666 | ret = overlay_adjust_local_conflicting_phandle(fdto, fdtonode,
667 | fdt_phandle);
668 | if (ret)
669 | return ret;
670 |
671 | ret = overlay_update_local_conflicting_references(fdto,
672 | fdt_phandle,
673 | fdto_phandle);
674 | if (ret)
675 | return ret;
676 | }
677 |
678 | fdt_for_each_subnode(fdtochild, fdto, fdtonode) {
679 | const char *name = fdt_get_name(fdto, fdtochild, NULL);
680 | int fdtchild;
681 | int ret;
682 |
683 | fdtchild = fdt_subnode_offset(fdt, fdtnode, name);
684 | if (fdtchild == -FDT_ERR_NOTFOUND)
685 | /*
686 | * no further overwrites possible here as this node is
687 | * new
688 | */
689 | continue;
690 |
691 | ret = overlay_prevent_phandle_overwrite_node(fdt, fdtchild,
692 | fdto, fdtochild);
693 | if (ret)
694 | return ret;
695 | }
696 |
697 | return 0;
698 | }
699 |
700 | /**
701 | * overlay_prevent_phandle_overwrite - Fixes overlay phandles to not overwrite base phandles
702 | * @fdt: Base Device Tree blob
703 | * @fdto: Device tree overlay blob
704 | *
705 | * Checks recursively if applying fdto overwrites phandle values in the base
706 | * dtb. When such a phandle is found, the fdto is changed to use the fdt's
707 | * phandle value to not break references in the base.
708 | *
709 | * returns:
710 | * 0 on success
711 | * Negative error code on failure
712 | */
713 | static int overlay_prevent_phandle_overwrite(void *fdt, void *fdto)
714 | {
715 | int fragment;
716 |
717 | fdt_for_each_subnode(fragment, fdto, 0) {
718 | int overlay;
719 | int target;
720 | int ret;
721 |
722 | overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
723 | if (overlay == -FDT_ERR_NOTFOUND)
724 | continue;
725 |
726 | if (overlay < 0)
727 | return overlay;
728 |
729 | target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
730 | if (target == -FDT_ERR_NOTFOUND)
731 | /*
732 | * The subtree doesn't exist in the base, so nothing
733 | * will be overwritten.
734 | */
735 | continue;
736 | else if (target < 0)
737 | return target;
738 |
739 | ret = overlay_prevent_phandle_overwrite_node(fdt, target,
740 | fdto, overlay);
741 | if (ret)
742 | return ret;
743 | }
744 |
745 | return 0;
746 | }
747 |
748 | /**
749 | * overlay_apply_node - Merges a node into the base device tree
750 | * @fdt: Base Device Tree blob
751 | * @target: Node offset in the base device tree to apply the fragment to
752 | * @fdto: Device tree overlay blob
753 | * @node: Node offset in the overlay holding the changes to merge
754 | *
755 | * overlay_apply_node() merges a node into a target base device tree
756 | * node pointed.
757 | *
758 | * This is part of the final step in the device tree overlay
759 | * application process, when all the phandles have been adjusted and
760 | * resolved and you just have to merge overlay into the base device
761 | * tree.
762 | *
763 | * returns:
764 | * 0 on success
765 | * Negative error code on failure
766 | */
767 | static int overlay_apply_node(void *fdt, int target,
768 | void *fdto, int node)
769 | {
770 | int property;
771 | int subnode;
772 |
773 | fdt_for_each_property_offset(property, fdto, node) {
774 | const char *name;
775 | const void *prop;
776 | int prop_len;
777 | int ret;
778 |
779 | prop = fdt_getprop_by_offset(fdto, property, &name,
780 | &prop_len);
781 | if (prop_len == -FDT_ERR_NOTFOUND)
782 | return -FDT_ERR_INTERNAL;
783 | if (prop_len < 0)
784 | return prop_len;
785 |
786 | ret = fdt_setprop(fdt, target, name, prop, prop_len);
787 | if (ret)
788 | return ret;
789 | }
790 |
791 | fdt_for_each_subnode(subnode, fdto, node) {
792 | const char *name = fdt_get_name(fdto, subnode, NULL);
793 | int nnode;
794 | int ret;
795 |
796 | nnode = fdt_add_subnode(fdt, target, name);
797 | if (nnode == -FDT_ERR_EXISTS) {
798 | nnode = fdt_subnode_offset(fdt, target, name);
799 | if (nnode == -FDT_ERR_NOTFOUND)
800 | return -FDT_ERR_INTERNAL;
801 | }
802 |
803 | if (nnode < 0)
804 | return nnode;
805 |
806 | ret = overlay_apply_node(fdt, nnode, fdto, subnode);
807 | if (ret)
808 | return ret;
809 | }
810 |
811 | return 0;
812 | }
813 |
814 | /**
815 | * overlay_merge - Merge an overlay into its base device tree
816 | * @fdt: Base Device Tree blob
817 | * @fdto: Device tree overlay blob
818 | *
819 | * overlay_merge() merges an overlay into its base device tree.
820 | *
821 | * This is the next to last step in the device tree overlay application
822 | * process, when all the phandles have been adjusted and resolved and
823 | * you just have to merge overlay into the base device tree.
824 | *
825 | * returns:
826 | * 0 on success
827 | * Negative error code on failure
828 | */
829 | static int overlay_merge(void *fdt, void *fdto)
830 | {
831 | int fragment;
832 |
833 | fdt_for_each_subnode(fragment, fdto, 0) {
834 | int overlay;
835 | int target;
836 | int ret;
837 |
838 | /*
839 | * Each fragments will have an __overlay__ node. If
840 | * they don't, it's not supposed to be merged
841 | */
842 | overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
843 | if (overlay == -FDT_ERR_NOTFOUND)
844 | continue;
845 |
846 | if (overlay < 0)
847 | return overlay;
848 |
849 | target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
850 | if (target < 0)
851 | return target;
852 |
853 | ret = overlay_apply_node(fdt, target, fdto, overlay);
854 | if (ret)
855 | return ret;
856 | }
857 |
858 | return 0;
859 | }
860 |
861 | static int get_path_len(const void *fdt, int nodeoffset)
862 | {
863 | int len = 0, namelen;
864 | const char *name;
865 |
866 | FDT_RO_PROBE(fdt);
867 |
868 | for (;;) {
869 | name = fdt_get_name(fdt, nodeoffset, &namelen);
870 | if (!name)
871 | return namelen;
872 |
873 | /* root? we're done */
874 | if (namelen == 0)
875 | break;
876 |
877 | nodeoffset = fdt_parent_offset(fdt, nodeoffset);
878 | if (nodeoffset < 0)
879 | return nodeoffset;
880 | len += namelen + 1;
881 | }
882 |
883 | /* in case of root pretend it's "/" */
884 | if (len == 0)
885 | len++;
886 | return len;
887 | }
888 |
889 | /**
890 | * overlay_symbol_update - Update the symbols of base tree after a merge
891 | * @fdt: Base Device Tree blob
892 | * @fdto: Device tree overlay blob
893 | *
894 | * overlay_symbol_update() updates the symbols of the base tree with the
895 | * symbols of the applied overlay
896 | *
897 | * This is the last step in the device tree overlay application
898 | * process, allowing the reference of overlay symbols by subsequent
899 | * overlay operations.
900 | *
901 | * returns:
902 | * 0 on success
903 | * Negative error code on failure
904 | */
905 | static int overlay_symbol_update(void *fdt, void *fdto)
906 | {
907 | int root_sym, ov_sym, prop, path_len, fragment, target;
908 | int len, frag_name_len, ret, rel_path_len;
909 | const char *s, *e;
910 | const char *path;
911 | const char *name;
912 | const char *frag_name;
913 | const char *rel_path;
914 | const char *target_path;
915 | char *buf;
916 | void *p;
917 |
918 | ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
919 |
920 | /* if no overlay symbols exist no problem */
921 | if (ov_sym < 0)
922 | return 0;
923 |
924 | root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
925 |
926 | /* it no root symbols exist we should create them */
927 | if (root_sym == -FDT_ERR_NOTFOUND)
928 | root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
929 |
930 | /* any error is fatal now */
931 | if (root_sym < 0)
932 | return root_sym;
933 |
934 | /* iterate over each overlay symbol */
935 | fdt_for_each_property_offset(prop, fdto, ov_sym) {
936 | path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
937 | if (!path)
938 | return path_len;
939 |
940 | /* verify it's a string property (terminated by a single \0) */
941 | if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
942 | return -FDT_ERR_BADVALUE;
943 |
944 | /* keep end marker to avoid strlen() */
945 | e = path + path_len;
946 |
947 | if (*path != '/')
948 | return -FDT_ERR_BADVALUE;
949 |
950 | /* get fragment name first */
951 | s = strchr(path + 1, '/');
952 | if (!s) {
953 | /* Symbol refers to something that won't end
954 | * up in the target tree */
955 | continue;
956 | }
957 |
958 | frag_name = path + 1;
959 | frag_name_len = s - path - 1;
960 |
961 | /* verify format; safe since "s" lies in \0 terminated prop */
962 | len = sizeof("/__overlay__/") - 1;
963 | if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) {
964 | /* //__overlay__/ */
965 | rel_path = s + len;
966 | rel_path_len = e - rel_path - 1;
967 | } else if ((e - s) == len
968 | && (memcmp(s, "/__overlay__", len - 1) == 0)) {
969 | /* //__overlay__ */
970 | rel_path = "";
971 | rel_path_len = 0;
972 | } else {
973 | /* Symbol refers to something that won't end
974 | * up in the target tree */
975 | continue;
976 | }
977 |
978 | /* find the fragment index in which the symbol lies */
979 | ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
980 | frag_name_len);
981 | /* not found? */
982 | if (ret < 0)
983 | return -FDT_ERR_BADOVERLAY;
984 | fragment = ret;
985 |
986 | /* an __overlay__ subnode must exist */
987 | ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
988 | if (ret < 0)
989 | return -FDT_ERR_BADOVERLAY;
990 |
991 | /* get the target of the fragment */
992 | ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
993 | if (ret < 0)
994 | return ret;
995 | target = ret;
996 |
997 | /* if we have a target path use */
998 | if (!target_path) {
999 | ret = get_path_len(fdt, target);
1000 | if (ret < 0)
1001 | return ret;
1002 | len = ret;
1003 | } else {
1004 | len = strlen(target_path);
1005 | }
1006 |
1007 | ret = fdt_setprop_placeholder(fdt, root_sym, name,
1008 | len + (len > 1) + rel_path_len + 1, &p);
1009 | if (ret < 0)
1010 | return ret;
1011 |
1012 | if (!target_path) {
1013 | /* again in case setprop_placeholder changed it */
1014 | ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
1015 | if (ret < 0)
1016 | return ret;
1017 | target = ret;
1018 | }
1019 |
1020 | buf = p;
1021 | if (len > 1) { /* target is not root */
1022 | if (!target_path) {
1023 | ret = fdt_get_path(fdt, target, buf, len + 1);
1024 | if (ret < 0)
1025 | return ret;
1026 | } else
1027 | memcpy(buf, target_path, len + 1);
1028 |
1029 | } else
1030 | len--;
1031 |
1032 | buf[len] = '/';
1033 | memcpy(buf + len + 1, rel_path, rel_path_len);
1034 | buf[len + 1 + rel_path_len] = '\0';
1035 | }
1036 |
1037 | return 0;
1038 | }
1039 |
1040 | int fdt_overlay_apply(void *fdt, void *fdto)
1041 | {
1042 | uint32_t delta;
1043 | int ret;
1044 |
1045 | FDT_RO_PROBE(fdt);
1046 | FDT_RO_PROBE(fdto);
1047 |
1048 | ret = fdt_find_max_phandle(fdt, &delta);
1049 | if (ret)
1050 | goto err;
1051 |
1052 | /* Increase all phandles in the fdto by delta */
1053 | ret = overlay_adjust_local_phandles(fdto, delta);
1054 | if (ret)
1055 | goto err;
1056 |
1057 | /* Adapt the phandle values in fdto to the above increase */
1058 | ret = overlay_update_local_references(fdto, delta);
1059 | if (ret)
1060 | goto err;
1061 |
1062 | /* Update fdto's phandles using symbols from fdt */
1063 | ret = overlay_fixup_phandles(fdt, fdto);
1064 | if (ret)
1065 | goto err;
1066 |
1067 | /* Don't overwrite phandles in fdt */
1068 | ret = overlay_prevent_phandle_overwrite(fdt, fdto);
1069 | if (ret)
1070 | goto err;
1071 |
1072 | ret = overlay_merge(fdt, fdto);
1073 | if (ret)
1074 | goto err;
1075 |
1076 | ret = overlay_symbol_update(fdt, fdto);
1077 | if (ret)
1078 | goto err;
1079 |
1080 | /*
1081 | * The overlay has been damaged, erase its magic.
1082 | */
1083 | fdt_set_magic(fdto, ~0);
1084 |
1085 | return 0;
1086 |
1087 | err:
1088 | /*
1089 | * The overlay might have been damaged, erase its magic.
1090 | */
1091 | fdt_set_magic(fdto, ~0);
1092 |
1093 | /*
1094 | * The base device tree might have been damaged, erase its
1095 | * magic.
1096 | */
1097 | fdt_set_magic(fdt, ~0);
1098 |
1099 | return ret;
1100 | }
1101 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_ro.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | static int fdt_nodename_eq_(const void *fdt, int offset,
14 | const char *s, int len)
15 | {
16 | int olen;
17 | const char *p = fdt_get_name(fdt, offset, &olen);
18 |
19 | if (!p || olen < len)
20 | /* short match */
21 | return 0;
22 |
23 | if (memcmp(p, s, len) != 0)
24 | return 0;
25 |
26 | if (p[len] == '\0')
27 | return 1;
28 | else if (!memchr(s, '@', len) && (p[len] == '@'))
29 | return 1;
30 | else
31 | return 0;
32 | }
33 |
34 | const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
35 | {
36 | int32_t totalsize;
37 | uint32_t absoffset;
38 | size_t len;
39 | int err;
40 | const char *s, *n;
41 |
42 | if (can_assume(VALID_INPUT)) {
43 | s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
44 |
45 | if (lenp)
46 | *lenp = strlen(s);
47 | return s;
48 | }
49 | totalsize = fdt_ro_probe_(fdt);
50 | err = totalsize;
51 | if (totalsize < 0)
52 | goto fail;
53 |
54 | err = -FDT_ERR_BADOFFSET;
55 | absoffset = stroffset + fdt_off_dt_strings(fdt);
56 | if (absoffset >= (unsigned)totalsize)
57 | goto fail;
58 | len = totalsize - absoffset;
59 |
60 | if (fdt_magic(fdt) == FDT_MAGIC) {
61 | if (stroffset < 0)
62 | goto fail;
63 | if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
64 | if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
65 | goto fail;
66 | if ((fdt_size_dt_strings(fdt) - stroffset) < len)
67 | len = fdt_size_dt_strings(fdt) - stroffset;
68 | }
69 | } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
70 | unsigned int sw_stroffset = -stroffset;
71 |
72 | if ((stroffset >= 0) ||
73 | (sw_stroffset > fdt_size_dt_strings(fdt)))
74 | goto fail;
75 | if (sw_stroffset < len)
76 | len = sw_stroffset;
77 | } else {
78 | err = -FDT_ERR_INTERNAL;
79 | goto fail;
80 | }
81 |
82 | s = (const char *)fdt + absoffset;
83 | n = memchr(s, '\0', len);
84 | if (!n) {
85 | /* missing terminating NULL */
86 | err = -FDT_ERR_TRUNCATED;
87 | goto fail;
88 | }
89 |
90 | if (lenp)
91 | *lenp = n - s;
92 | return s;
93 |
94 | fail:
95 | if (lenp)
96 | *lenp = err;
97 | return NULL;
98 | }
99 |
100 | const char *fdt_string(const void *fdt, int stroffset)
101 | {
102 | return fdt_get_string(fdt, stroffset, NULL);
103 | }
104 |
105 | static int fdt_string_eq_(const void *fdt, int stroffset,
106 | const char *s, int len)
107 | {
108 | int slen;
109 | const char *p = fdt_get_string(fdt, stroffset, &slen);
110 |
111 | return p && (slen == len) && (memcmp(p, s, len) == 0);
112 | }
113 |
114 | int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
115 | {
116 | uint32_t max = 0;
117 | int offset = -1;
118 |
119 | while (true) {
120 | uint32_t value;
121 |
122 | offset = fdt_next_node(fdt, offset, NULL);
123 | if (offset < 0) {
124 | if (offset == -FDT_ERR_NOTFOUND)
125 | break;
126 |
127 | return offset;
128 | }
129 |
130 | value = fdt_get_phandle(fdt, offset);
131 |
132 | if (value > max)
133 | max = value;
134 | }
135 |
136 | if (phandle)
137 | *phandle = max;
138 |
139 | return 0;
140 | }
141 |
142 | int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
143 | {
144 | uint32_t max;
145 | int err;
146 |
147 | err = fdt_find_max_phandle(fdt, &max);
148 | if (err < 0)
149 | return err;
150 |
151 | if (max == FDT_MAX_PHANDLE)
152 | return -FDT_ERR_NOPHANDLES;
153 |
154 | if (phandle)
155 | *phandle = max + 1;
156 |
157 | return 0;
158 | }
159 |
160 | static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
161 | {
162 | unsigned int offset = n * sizeof(struct fdt_reserve_entry);
163 | unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
164 |
165 | if (!can_assume(VALID_INPUT)) {
166 | if (absoffset < fdt_off_mem_rsvmap(fdt))
167 | return NULL;
168 | if (absoffset > fdt_totalsize(fdt) -
169 | sizeof(struct fdt_reserve_entry))
170 | return NULL;
171 | }
172 | return fdt_mem_rsv_(fdt, n);
173 | }
174 |
175 | int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
176 | {
177 | const struct fdt_reserve_entry *re;
178 |
179 | FDT_RO_PROBE(fdt);
180 | re = fdt_mem_rsv(fdt, n);
181 | if (!can_assume(VALID_INPUT) && !re)
182 | return -FDT_ERR_BADOFFSET;
183 |
184 | *address = fdt64_ld_(&re->address);
185 | *size = fdt64_ld_(&re->size);
186 | return 0;
187 | }
188 |
189 | int fdt_num_mem_rsv(const void *fdt)
190 | {
191 | int i;
192 | const struct fdt_reserve_entry *re;
193 |
194 | for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
195 | if (fdt64_ld_(&re->size) == 0)
196 | return i;
197 | }
198 | return -FDT_ERR_TRUNCATED;
199 | }
200 |
201 | static int nextprop_(const void *fdt, int offset)
202 | {
203 | uint32_t tag;
204 | int nextoffset;
205 |
206 | do {
207 | tag = fdt_next_tag(fdt, offset, &nextoffset);
208 |
209 | switch (tag) {
210 | case FDT_END:
211 | if (nextoffset >= 0)
212 | return -FDT_ERR_BADSTRUCTURE;
213 | else
214 | return nextoffset;
215 |
216 | case FDT_PROP:
217 | return offset;
218 | }
219 | offset = nextoffset;
220 | } while (tag == FDT_NOP);
221 |
222 | return -FDT_ERR_NOTFOUND;
223 | }
224 |
225 | int fdt_subnode_offset_namelen(const void *fdt, int offset,
226 | const char *name, int namelen)
227 | {
228 | int depth;
229 |
230 | FDT_RO_PROBE(fdt);
231 |
232 | for (depth = 0;
233 | (offset >= 0) && (depth >= 0);
234 | offset = fdt_next_node(fdt, offset, &depth))
235 | if ((depth == 1)
236 | && fdt_nodename_eq_(fdt, offset, name, namelen))
237 | return offset;
238 |
239 | if (depth < 0)
240 | return -FDT_ERR_NOTFOUND;
241 | return offset; /* error */
242 | }
243 |
244 | int fdt_subnode_offset(const void *fdt, int parentoffset,
245 | const char *name)
246 | {
247 | return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
248 | }
249 |
250 | int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
251 | {
252 | const char *end = path + namelen;
253 | const char *p = path;
254 | int offset = 0;
255 |
256 | FDT_RO_PROBE(fdt);
257 |
258 | if (!can_assume(VALID_INPUT) && namelen <= 0)
259 | return -FDT_ERR_BADPATH;
260 |
261 | /* see if we have an alias */
262 | if (*path != '/') {
263 | const char *q = memchr(path, '/', end - p);
264 |
265 | if (!q)
266 | q = end;
267 |
268 | p = fdt_get_alias_namelen(fdt, p, q - p);
269 | if (!p)
270 | return -FDT_ERR_BADPATH;
271 | offset = fdt_path_offset(fdt, p);
272 |
273 | p = q;
274 | }
275 |
276 | while (p < end) {
277 | const char *q;
278 |
279 | while (*p == '/') {
280 | p++;
281 | if (p == end)
282 | return offset;
283 | }
284 | q = memchr(p, '/', end - p);
285 | if (! q)
286 | q = end;
287 |
288 | offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
289 | if (offset < 0)
290 | return offset;
291 |
292 | p = q;
293 | }
294 |
295 | return offset;
296 | }
297 |
298 | int fdt_path_offset(const void *fdt, const char *path)
299 | {
300 | return fdt_path_offset_namelen(fdt, path, strlen(path));
301 | }
302 |
303 | const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
304 | {
305 | const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
306 | const char *nameptr;
307 | int err;
308 |
309 | if (((err = fdt_ro_probe_(fdt)) < 0)
310 | || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
311 | goto fail;
312 |
313 | nameptr = nh->name;
314 |
315 | if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
316 | /*
317 | * For old FDT versions, match the naming conventions of V16:
318 | * give only the leaf name (after all /). The actual tree
319 | * contents are loosely checked.
320 | */
321 | const char *leaf;
322 | leaf = strrchr(nameptr, '/');
323 | if (leaf == NULL) {
324 | err = -FDT_ERR_BADSTRUCTURE;
325 | goto fail;
326 | }
327 | nameptr = leaf+1;
328 | }
329 |
330 | if (len)
331 | *len = strlen(nameptr);
332 |
333 | return nameptr;
334 |
335 | fail:
336 | if (len)
337 | *len = err;
338 | return NULL;
339 | }
340 |
341 | int fdt_first_property_offset(const void *fdt, int nodeoffset)
342 | {
343 | int offset;
344 |
345 | if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
346 | return offset;
347 |
348 | return nextprop_(fdt, offset);
349 | }
350 |
351 | int fdt_next_property_offset(const void *fdt, int offset)
352 | {
353 | if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
354 | return offset;
355 |
356 | return nextprop_(fdt, offset);
357 | }
358 |
359 | static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
360 | int offset,
361 | int *lenp)
362 | {
363 | int err;
364 | const struct fdt_property *prop;
365 |
366 | if (!can_assume(VALID_INPUT) &&
367 | (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
368 | if (lenp)
369 | *lenp = err;
370 | return NULL;
371 | }
372 |
373 | prop = fdt_offset_ptr_(fdt, offset);
374 |
375 | if (lenp)
376 | *lenp = fdt32_ld_(&prop->len);
377 |
378 | return prop;
379 | }
380 |
381 | const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
382 | int offset,
383 | int *lenp)
384 | {
385 | /* Prior to version 16, properties may need realignment
386 | * and this API does not work. fdt_getprop_*() will, however. */
387 |
388 | if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
389 | if (lenp)
390 | *lenp = -FDT_ERR_BADVERSION;
391 | return NULL;
392 | }
393 |
394 | return fdt_get_property_by_offset_(fdt, offset, lenp);
395 | }
396 |
397 | static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
398 | int offset,
399 | const char *name,
400 | int namelen,
401 | int *lenp,
402 | int *poffset)
403 | {
404 | for (offset = fdt_first_property_offset(fdt, offset);
405 | (offset >= 0);
406 | (offset = fdt_next_property_offset(fdt, offset))) {
407 | const struct fdt_property *prop;
408 |
409 | prop = fdt_get_property_by_offset_(fdt, offset, lenp);
410 | if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
411 | offset = -FDT_ERR_INTERNAL;
412 | break;
413 | }
414 | if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
415 | name, namelen)) {
416 | if (poffset)
417 | *poffset = offset;
418 | return prop;
419 | }
420 | }
421 |
422 | if (lenp)
423 | *lenp = offset;
424 | return NULL;
425 | }
426 |
427 |
428 | const struct fdt_property *fdt_get_property_namelen(const void *fdt,
429 | int offset,
430 | const char *name,
431 | int namelen, int *lenp)
432 | {
433 | /* Prior to version 16, properties may need realignment
434 | * and this API does not work. fdt_getprop_*() will, however. */
435 | if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
436 | if (lenp)
437 | *lenp = -FDT_ERR_BADVERSION;
438 | return NULL;
439 | }
440 |
441 | return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
442 | NULL);
443 | }
444 |
445 |
446 | const struct fdt_property *fdt_get_property(const void *fdt,
447 | int nodeoffset,
448 | const char *name, int *lenp)
449 | {
450 | return fdt_get_property_namelen(fdt, nodeoffset, name,
451 | strlen(name), lenp);
452 | }
453 |
454 | const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
455 | const char *name, int namelen, int *lenp)
456 | {
457 | int poffset;
458 | const struct fdt_property *prop;
459 |
460 | prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
461 | &poffset);
462 | if (!prop)
463 | return NULL;
464 |
465 | /* Handle realignment */
466 | if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
467 | (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
468 | return prop->data + 4;
469 | return prop->data;
470 | }
471 |
472 | const void *fdt_getprop_by_offset(const void *fdt, int offset,
473 | const char **namep, int *lenp)
474 | {
475 | const struct fdt_property *prop;
476 |
477 | prop = fdt_get_property_by_offset_(fdt, offset, lenp);
478 | if (!prop)
479 | return NULL;
480 | if (namep) {
481 | const char *name;
482 | int namelen;
483 |
484 | if (!can_assume(VALID_INPUT)) {
485 | name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
486 | &namelen);
487 | *namep = name;
488 | if (!name) {
489 | if (lenp)
490 | *lenp = namelen;
491 | return NULL;
492 | }
493 | } else {
494 | *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
495 | }
496 | }
497 |
498 | /* Handle realignment */
499 | if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
500 | (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
501 | return prop->data + 4;
502 | return prop->data;
503 | }
504 |
505 | const void *fdt_getprop(const void *fdt, int nodeoffset,
506 | const char *name, int *lenp)
507 | {
508 | return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
509 | }
510 |
511 | uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
512 | {
513 | const fdt32_t *php;
514 | int len;
515 |
516 | /* FIXME: This is a bit sub-optimal, since we potentially scan
517 | * over all the properties twice. */
518 | php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
519 | if (!php || (len != sizeof(*php))) {
520 | php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
521 | if (!php || (len != sizeof(*php)))
522 | return 0;
523 | }
524 |
525 | return fdt32_ld_(php);
526 | }
527 |
528 | static const void *fdt_path_getprop_namelen(const void *fdt, const char *path,
529 | const char *propname, int propnamelen,
530 | int *lenp)
531 | {
532 | int offset = fdt_path_offset(fdt, path);
533 |
534 | if (offset < 0)
535 | return NULL;
536 |
537 | return fdt_getprop_namelen(fdt, offset, propname, propnamelen, lenp);
538 | }
539 |
540 | const char *fdt_get_alias_namelen(const void *fdt,
541 | const char *name, int namelen)
542 | {
543 | int len;
544 | const char *alias;
545 |
546 | alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len);
547 |
548 | if (!can_assume(VALID_DTB) &&
549 | !(alias && len > 0 && alias[len - 1] == '\0' && *alias == '/'))
550 | return NULL;
551 |
552 | return alias;
553 | }
554 |
555 | const char *fdt_get_alias(const void *fdt, const char *name)
556 | {
557 | return fdt_get_alias_namelen(fdt, name, strlen(name));
558 | }
559 |
560 | const char *fdt_get_symbol_namelen(const void *fdt,
561 | const char *name, int namelen)
562 | {
563 | return fdt_path_getprop_namelen(fdt, "/__symbols__", name, namelen, NULL);
564 | }
565 |
566 | const char *fdt_get_symbol(const void *fdt, const char *name)
567 | {
568 | return fdt_get_symbol_namelen(fdt, name, strlen(name));
569 | }
570 |
571 | int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
572 | {
573 | int pdepth = 0, p = 0;
574 | int offset, depth, namelen;
575 | const char *name;
576 |
577 | FDT_RO_PROBE(fdt);
578 |
579 | if (buflen < 2)
580 | return -FDT_ERR_NOSPACE;
581 |
582 | for (offset = 0, depth = 0;
583 | (offset >= 0) && (offset <= nodeoffset);
584 | offset = fdt_next_node(fdt, offset, &depth)) {
585 | while (pdepth > depth) {
586 | do {
587 | p--;
588 | } while (buf[p-1] != '/');
589 | pdepth--;
590 | }
591 |
592 | if (pdepth >= depth) {
593 | name = fdt_get_name(fdt, offset, &namelen);
594 | if (!name)
595 | return namelen;
596 | if ((p + namelen + 1) <= buflen) {
597 | memcpy(buf + p, name, namelen);
598 | p += namelen;
599 | buf[p++] = '/';
600 | pdepth++;
601 | }
602 | }
603 |
604 | if (offset == nodeoffset) {
605 | if (pdepth < (depth + 1))
606 | return -FDT_ERR_NOSPACE;
607 |
608 | if (p > 1) /* special case so that root path is "/", not "" */
609 | p--;
610 | buf[p] = '\0';
611 | return 0;
612 | }
613 | }
614 |
615 | if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
616 | return -FDT_ERR_BADOFFSET;
617 | else if (offset == -FDT_ERR_BADOFFSET)
618 | return -FDT_ERR_BADSTRUCTURE;
619 |
620 | return offset; /* error from fdt_next_node() */
621 | }
622 |
623 | int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
624 | int supernodedepth, int *nodedepth)
625 | {
626 | int offset, depth;
627 | int supernodeoffset = -FDT_ERR_INTERNAL;
628 |
629 | FDT_RO_PROBE(fdt);
630 |
631 | if (supernodedepth < 0)
632 | return -FDT_ERR_NOTFOUND;
633 |
634 | for (offset = 0, depth = 0;
635 | (offset >= 0) && (offset <= nodeoffset);
636 | offset = fdt_next_node(fdt, offset, &depth)) {
637 | if (depth == supernodedepth)
638 | supernodeoffset = offset;
639 |
640 | if (offset == nodeoffset) {
641 | if (nodedepth)
642 | *nodedepth = depth;
643 |
644 | if (supernodedepth > depth)
645 | return -FDT_ERR_NOTFOUND;
646 | else
647 | return supernodeoffset;
648 | }
649 | }
650 |
651 | if (!can_assume(VALID_INPUT)) {
652 | if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
653 | return -FDT_ERR_BADOFFSET;
654 | else if (offset == -FDT_ERR_BADOFFSET)
655 | return -FDT_ERR_BADSTRUCTURE;
656 | }
657 |
658 | return offset; /* error from fdt_next_node() */
659 | }
660 |
661 | int fdt_node_depth(const void *fdt, int nodeoffset)
662 | {
663 | int nodedepth;
664 | int err;
665 |
666 | err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
667 | if (err)
668 | return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
669 | -FDT_ERR_INTERNAL;
670 | return nodedepth;
671 | }
672 |
673 | int fdt_parent_offset(const void *fdt, int nodeoffset)
674 | {
675 | int nodedepth = fdt_node_depth(fdt, nodeoffset);
676 |
677 | if (nodedepth < 0)
678 | return nodedepth;
679 | return fdt_supernode_atdepth_offset(fdt, nodeoffset,
680 | nodedepth - 1, NULL);
681 | }
682 |
683 | int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
684 | const char *propname,
685 | const void *propval, int proplen)
686 | {
687 | int offset;
688 | const void *val;
689 | int len;
690 |
691 | FDT_RO_PROBE(fdt);
692 |
693 | /* FIXME: The algorithm here is pretty horrible: we scan each
694 | * property of a node in fdt_getprop(), then if that didn't
695 | * find what we want, we scan over them again making our way
696 | * to the next node. Still it's the easiest to implement
697 | * approach; performance can come later. */
698 | for (offset = fdt_next_node(fdt, startoffset, NULL);
699 | offset >= 0;
700 | offset = fdt_next_node(fdt, offset, NULL)) {
701 | val = fdt_getprop(fdt, offset, propname, &len);
702 | if (val && (len == proplen)
703 | && (memcmp(val, propval, len) == 0))
704 | return offset;
705 | }
706 |
707 | return offset; /* error from fdt_next_node() */
708 | }
709 |
710 | int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
711 | {
712 | int offset;
713 |
714 | if ((phandle == 0) || (phandle == ~0U))
715 | return -FDT_ERR_BADPHANDLE;
716 |
717 | FDT_RO_PROBE(fdt);
718 |
719 | /* FIXME: The algorithm here is pretty horrible: we
720 | * potentially scan each property of a node in
721 | * fdt_get_phandle(), then if that didn't find what
722 | * we want, we scan over them again making our way to the next
723 | * node. Still it's the easiest to implement approach;
724 | * performance can come later. */
725 | for (offset = fdt_next_node(fdt, -1, NULL);
726 | offset >= 0;
727 | offset = fdt_next_node(fdt, offset, NULL)) {
728 | if (fdt_get_phandle(fdt, offset) == phandle)
729 | return offset;
730 | }
731 |
732 | return offset; /* error from fdt_next_node() */
733 | }
734 |
735 | int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
736 | {
737 | int len = strlen(str);
738 | const char *p;
739 |
740 | while (listlen >= len) {
741 | if (memcmp(str, strlist, len+1) == 0)
742 | return 1;
743 | p = memchr(strlist, '\0', listlen);
744 | if (!p)
745 | return 0; /* malformed strlist.. */
746 | listlen -= (p-strlist) + 1;
747 | strlist = p + 1;
748 | }
749 | return 0;
750 | }
751 |
752 | int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
753 | {
754 | const char *list, *end;
755 | int length, count = 0;
756 |
757 | list = fdt_getprop(fdt, nodeoffset, property, &length);
758 | if (!list)
759 | return length;
760 |
761 | end = list + length;
762 |
763 | while (list < end) {
764 | length = strnlen(list, end - list) + 1;
765 |
766 | /* Abort if the last string isn't properly NUL-terminated. */
767 | if (list + length > end)
768 | return -FDT_ERR_BADVALUE;
769 |
770 | list += length;
771 | count++;
772 | }
773 |
774 | return count;
775 | }
776 |
777 | int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
778 | const char *string)
779 | {
780 | int length, len, idx = 0;
781 | const char *list, *end;
782 |
783 | list = fdt_getprop(fdt, nodeoffset, property, &length);
784 | if (!list)
785 | return length;
786 |
787 | len = strlen(string) + 1;
788 | end = list + length;
789 |
790 | while (list < end) {
791 | length = strnlen(list, end - list) + 1;
792 |
793 | /* Abort if the last string isn't properly NUL-terminated. */
794 | if (list + length > end)
795 | return -FDT_ERR_BADVALUE;
796 |
797 | if (length == len && memcmp(list, string, length) == 0)
798 | return idx;
799 |
800 | list += length;
801 | idx++;
802 | }
803 |
804 | return -FDT_ERR_NOTFOUND;
805 | }
806 |
807 | const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
808 | const char *property, int idx,
809 | int *lenp)
810 | {
811 | const char *list, *end;
812 | int length;
813 |
814 | list = fdt_getprop(fdt, nodeoffset, property, &length);
815 | if (!list) {
816 | if (lenp)
817 | *lenp = length;
818 |
819 | return NULL;
820 | }
821 |
822 | end = list + length;
823 |
824 | while (list < end) {
825 | length = strnlen(list, end - list) + 1;
826 |
827 | /* Abort if the last string isn't properly NUL-terminated. */
828 | if (list + length > end) {
829 | if (lenp)
830 | *lenp = -FDT_ERR_BADVALUE;
831 |
832 | return NULL;
833 | }
834 |
835 | if (idx == 0) {
836 | if (lenp)
837 | *lenp = length - 1;
838 |
839 | return list;
840 | }
841 |
842 | list += length;
843 | idx--;
844 | }
845 |
846 | if (lenp)
847 | *lenp = -FDT_ERR_NOTFOUND;
848 |
849 | return NULL;
850 | }
851 |
852 | int fdt_node_check_compatible(const void *fdt, int nodeoffset,
853 | const char *compatible)
854 | {
855 | const void *prop;
856 | int len;
857 |
858 | prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
859 | if (!prop)
860 | return len;
861 |
862 | return !fdt_stringlist_contains(prop, len, compatible);
863 | }
864 |
865 | int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
866 | const char *compatible)
867 | {
868 | int offset, err;
869 |
870 | FDT_RO_PROBE(fdt);
871 |
872 | /* FIXME: The algorithm here is pretty horrible: we scan each
873 | * property of a node in fdt_node_check_compatible(), then if
874 | * that didn't find what we want, we scan over them again
875 | * making our way to the next node. Still it's the easiest to
876 | * implement approach; performance can come later. */
877 | for (offset = fdt_next_node(fdt, startoffset, NULL);
878 | offset >= 0;
879 | offset = fdt_next_node(fdt, offset, NULL)) {
880 | err = fdt_node_check_compatible(fdt, offset, compatible);
881 | if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
882 | return err;
883 | else if (err == 0)
884 | return offset;
885 | }
886 |
887 | return offset; /* error from fdt_next_node() */
888 | }
889 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_rw.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | static int fdt_blocks_misordered_(const void *fdt,
14 | int mem_rsv_size, int struct_size)
15 | {
16 | return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
17 | || (fdt_off_dt_struct(fdt) <
18 | (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
19 | || (fdt_off_dt_strings(fdt) <
20 | (fdt_off_dt_struct(fdt) + struct_size))
21 | || (fdt_totalsize(fdt) <
22 | (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
23 | }
24 |
25 | static int fdt_rw_probe_(void *fdt)
26 | {
27 | if (can_assume(VALID_DTB))
28 | return 0;
29 | FDT_RO_PROBE(fdt);
30 |
31 | if (!can_assume(LATEST) && fdt_version(fdt) < 17)
32 | return -FDT_ERR_BADVERSION;
33 | if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
34 | fdt_size_dt_struct(fdt)))
35 | return -FDT_ERR_BADLAYOUT;
36 | if (!can_assume(LATEST) && fdt_version(fdt) > 17)
37 | fdt_set_version(fdt, 17);
38 |
39 | return 0;
40 | }
41 |
42 | #define FDT_RW_PROBE(fdt) \
43 | { \
44 | int err_; \
45 | if ((err_ = fdt_rw_probe_(fdt)) != 0) \
46 | return err_; \
47 | }
48 |
49 | static inline unsigned int fdt_data_size_(void *fdt)
50 | {
51 | return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
52 | }
53 |
54 | static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
55 | {
56 | char *p = splicepoint;
57 | unsigned int dsize = fdt_data_size_(fdt);
58 | size_t soff = p - (char *)fdt;
59 |
60 | if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
61 | return -FDT_ERR_BADOFFSET;
62 | if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
63 | return -FDT_ERR_BADOFFSET;
64 | if (dsize - oldlen + newlen > fdt_totalsize(fdt))
65 | return -FDT_ERR_NOSPACE;
66 | memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
67 | return 0;
68 | }
69 |
70 | static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
71 | int oldn, int newn)
72 | {
73 | int delta = (newn - oldn) * sizeof(*p);
74 | int err;
75 | err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
76 | if (err)
77 | return err;
78 | fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
79 | fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
80 | return 0;
81 | }
82 |
83 | static int fdt_splice_struct_(void *fdt, void *p,
84 | int oldlen, int newlen)
85 | {
86 | int delta = newlen - oldlen;
87 | int err;
88 |
89 | if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
90 | return err;
91 |
92 | fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
93 | fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
94 | return 0;
95 | }
96 |
97 | /* Must only be used to roll back in case of error */
98 | static void fdt_del_last_string_(void *fdt, const char *s)
99 | {
100 | int newlen = strlen(s) + 1;
101 |
102 | fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
103 | }
104 |
105 | static int fdt_splice_string_(void *fdt, int newlen)
106 | {
107 | void *p = (char *)fdt
108 | + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
109 | int err;
110 |
111 | if ((err = fdt_splice_(fdt, p, 0, newlen)))
112 | return err;
113 |
114 | fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
115 | return 0;
116 | }
117 |
118 | /**
119 | * fdt_find_add_string_() - Find or allocate a string
120 | *
121 | * @fdt: pointer to the device tree to check/adjust
122 | * @s: string to find/add
123 | * @allocated: Set to 0 if the string was found, 1 if not found and so
124 | * allocated. Ignored if can_assume(NO_ROLLBACK)
125 | * @return offset of string in the string table (whether found or added)
126 | */
127 | static int fdt_find_add_string_(void *fdt, const char *s, int slen,
128 | int *allocated)
129 | {
130 | char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
131 | const char *p;
132 | char *new;
133 | int err;
134 |
135 | if (!can_assume(NO_ROLLBACK))
136 | *allocated = 0;
137 |
138 | p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);
139 | if (p)
140 | /* found it */
141 | return (p - strtab);
142 |
143 | new = strtab + fdt_size_dt_strings(fdt);
144 | err = fdt_splice_string_(fdt, slen + 1);
145 | if (err)
146 | return err;
147 |
148 | if (!can_assume(NO_ROLLBACK))
149 | *allocated = 1;
150 |
151 | memcpy(new, s, slen);
152 | new[slen] = '\0';
153 |
154 | return (new - strtab);
155 | }
156 |
157 | int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
158 | {
159 | struct fdt_reserve_entry *re;
160 | int err;
161 |
162 | FDT_RW_PROBE(fdt);
163 |
164 | re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
165 | err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
166 | if (err)
167 | return err;
168 |
169 | re->address = cpu_to_fdt64(address);
170 | re->size = cpu_to_fdt64(size);
171 | return 0;
172 | }
173 |
174 | int fdt_del_mem_rsv(void *fdt, int n)
175 | {
176 | struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
177 |
178 | FDT_RW_PROBE(fdt);
179 |
180 | if (n >= fdt_num_mem_rsv(fdt))
181 | return -FDT_ERR_NOTFOUND;
182 |
183 | return fdt_splice_mem_rsv_(fdt, re, 1, 0);
184 | }
185 |
186 | static int fdt_resize_property_(void *fdt, int nodeoffset,
187 | const char *name, int namelen,
188 | int len, struct fdt_property **prop)
189 | {
190 | int oldlen;
191 | int err;
192 |
193 | *prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,
194 | &oldlen);
195 | if (!*prop)
196 | return oldlen;
197 |
198 | if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
199 | FDT_TAGALIGN(len))))
200 | return err;
201 |
202 | (*prop)->len = cpu_to_fdt32(len);
203 | return 0;
204 | }
205 |
206 | static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
207 | int namelen, int len, struct fdt_property **prop)
208 | {
209 | int proplen;
210 | int nextoffset;
211 | int namestroff;
212 | int err;
213 | int allocated;
214 |
215 | if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
216 | return nextoffset;
217 |
218 | namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
219 | if (namestroff < 0)
220 | return namestroff;
221 |
222 | *prop = fdt_offset_ptr_w_(fdt, nextoffset);
223 | proplen = sizeof(**prop) + FDT_TAGALIGN(len);
224 |
225 | err = fdt_splice_struct_(fdt, *prop, 0, proplen);
226 | if (err) {
227 | /* Delete the string if we failed to add it */
228 | if (!can_assume(NO_ROLLBACK) && allocated)
229 | fdt_del_last_string_(fdt, name);
230 | return err;
231 | }
232 |
233 | (*prop)->tag = cpu_to_fdt32(FDT_PROP);
234 | (*prop)->nameoff = cpu_to_fdt32(namestroff);
235 | (*prop)->len = cpu_to_fdt32(len);
236 | return 0;
237 | }
238 |
239 | int fdt_set_name(void *fdt, int nodeoffset, const char *name)
240 | {
241 | char *namep;
242 | int oldlen, newlen;
243 | int err;
244 |
245 | FDT_RW_PROBE(fdt);
246 |
247 | namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
248 | if (!namep)
249 | return oldlen;
250 |
251 | newlen = strlen(name);
252 |
253 | err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
254 | FDT_TAGALIGN(newlen+1));
255 | if (err)
256 | return err;
257 |
258 | memcpy(namep, name, newlen+1);
259 | return 0;
260 | }
261 |
262 | int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
263 | int namelen, int len, void **prop_data)
264 | {
265 | struct fdt_property *prop;
266 | int err;
267 |
268 | FDT_RW_PROBE(fdt);
269 |
270 | err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);
271 | if (err == -FDT_ERR_NOTFOUND)
272 | err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,
273 | &prop);
274 | if (err)
275 | return err;
276 |
277 | *prop_data = prop->data;
278 | return 0;
279 | }
280 |
281 | int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
282 | int namelen, const void *val, int len)
283 | {
284 | void *prop_data;
285 | int err;
286 |
287 | err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,
288 | len, &prop_data);
289 | if (err)
290 | return err;
291 |
292 | if (len)
293 | memcpy(prop_data, val, len);
294 | return 0;
295 | }
296 |
297 | int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
298 | const void *val, int len)
299 | {
300 | struct fdt_property *prop;
301 | int err, oldlen, newlen;
302 |
303 | FDT_RW_PROBE(fdt);
304 |
305 | prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
306 | if (prop) {
307 | newlen = len + oldlen;
308 | err = fdt_splice_struct_(fdt, prop->data,
309 | FDT_TAGALIGN(oldlen),
310 | FDT_TAGALIGN(newlen));
311 | if (err)
312 | return err;
313 | prop->len = cpu_to_fdt32(newlen);
314 | memcpy(prop->data + oldlen, val, len);
315 | } else {
316 | err = fdt_add_property_(fdt, nodeoffset, name, strlen(name),
317 | len, &prop);
318 | if (err)
319 | return err;
320 | memcpy(prop->data, val, len);
321 | }
322 | return 0;
323 | }
324 |
325 | int fdt_delprop(void *fdt, int nodeoffset, const char *name)
326 | {
327 | struct fdt_property *prop;
328 | int len, proplen;
329 |
330 | FDT_RW_PROBE(fdt);
331 |
332 | prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
333 | if (!prop)
334 | return len;
335 |
336 | proplen = sizeof(*prop) + FDT_TAGALIGN(len);
337 | return fdt_splice_struct_(fdt, prop, proplen, 0);
338 | }
339 |
340 | int fdt_add_subnode_namelen(void *fdt, int parentoffset,
341 | const char *name, int namelen)
342 | {
343 | struct fdt_node_header *nh;
344 | int offset, nextoffset;
345 | int nodelen;
346 | int err;
347 | uint32_t tag;
348 | fdt32_t *endtag;
349 |
350 | FDT_RW_PROBE(fdt);
351 |
352 | offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
353 | if (offset >= 0)
354 | return -FDT_ERR_EXISTS;
355 | else if (offset != -FDT_ERR_NOTFOUND)
356 | return offset;
357 |
358 | /* Try to place the new node after the parent's properties */
359 | tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
360 | /* the fdt_subnode_offset_namelen() should ensure this never hits */
361 | if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
362 | return -FDT_ERR_INTERNAL;
363 | do {
364 | offset = nextoffset;
365 | tag = fdt_next_tag(fdt, offset, &nextoffset);
366 | } while ((tag == FDT_PROP) || (tag == FDT_NOP));
367 |
368 | nh = fdt_offset_ptr_w_(fdt, offset);
369 | nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
370 |
371 | err = fdt_splice_struct_(fdt, nh, 0, nodelen);
372 | if (err)
373 | return err;
374 |
375 | nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
376 | memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
377 | memcpy(nh->name, name, namelen);
378 | endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
379 | *endtag = cpu_to_fdt32(FDT_END_NODE);
380 |
381 | return offset;
382 | }
383 |
384 | int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
385 | {
386 | return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
387 | }
388 |
389 | int fdt_del_node(void *fdt, int nodeoffset)
390 | {
391 | int endoffset;
392 |
393 | FDT_RW_PROBE(fdt);
394 |
395 | endoffset = fdt_node_end_offset_(fdt, nodeoffset);
396 | if (endoffset < 0)
397 | return endoffset;
398 |
399 | return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
400 | endoffset - nodeoffset, 0);
401 | }
402 |
403 | static void fdt_packblocks_(const char *old, char *new,
404 | int mem_rsv_size,
405 | int struct_size,
406 | int strings_size)
407 | {
408 | int mem_rsv_off, struct_off, strings_off;
409 |
410 | mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
411 | struct_off = mem_rsv_off + mem_rsv_size;
412 | strings_off = struct_off + struct_size;
413 |
414 | memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
415 | fdt_set_off_mem_rsvmap(new, mem_rsv_off);
416 |
417 | memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
418 | fdt_set_off_dt_struct(new, struct_off);
419 | fdt_set_size_dt_struct(new, struct_size);
420 |
421 | memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
422 | fdt_set_off_dt_strings(new, strings_off);
423 | fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
424 | }
425 |
426 | int fdt_open_into(const void *fdt, void *buf, int bufsize)
427 | {
428 | int err;
429 | int mem_rsv_size, struct_size;
430 | int newsize;
431 | const char *fdtstart = fdt;
432 | const char *fdtend = fdtstart + fdt_totalsize(fdt);
433 | char *tmp;
434 |
435 | FDT_RO_PROBE(fdt);
436 |
437 | mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
438 | * sizeof(struct fdt_reserve_entry);
439 |
440 | if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
441 | struct_size = fdt_size_dt_struct(fdt);
442 | } else if (fdt_version(fdt) == 16) {
443 | struct_size = 0;
444 | while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
445 | ;
446 | if (struct_size < 0)
447 | return struct_size;
448 | } else {
449 | return -FDT_ERR_BADVERSION;
450 | }
451 |
452 | if (can_assume(LIBFDT_ORDER) ||
453 | !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
454 | /* no further work necessary */
455 | err = fdt_move(fdt, buf, bufsize);
456 | if (err)
457 | return err;
458 | fdt_set_version(buf, 17);
459 | fdt_set_size_dt_struct(buf, struct_size);
460 | fdt_set_totalsize(buf, bufsize);
461 | return 0;
462 | }
463 |
464 | /* Need to reorder */
465 | newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
466 | + struct_size + fdt_size_dt_strings(fdt);
467 |
468 | if (bufsize < newsize)
469 | return -FDT_ERR_NOSPACE;
470 |
471 | /* First attempt to build converted tree at beginning of buffer */
472 | tmp = buf;
473 | /* But if that overlaps with the old tree... */
474 | if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
475 | /* Try right after the old tree instead */
476 | tmp = (char *)(uintptr_t)fdtend;
477 | if ((tmp + newsize) > ((char *)buf + bufsize))
478 | return -FDT_ERR_NOSPACE;
479 | }
480 |
481 | fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
482 | fdt_size_dt_strings(fdt));
483 | memmove(buf, tmp, newsize);
484 |
485 | fdt_set_magic(buf, FDT_MAGIC);
486 | fdt_set_totalsize(buf, bufsize);
487 | fdt_set_version(buf, 17);
488 | fdt_set_last_comp_version(buf, 16);
489 | fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
490 |
491 | return 0;
492 | }
493 |
494 | int fdt_pack(void *fdt)
495 | {
496 | int mem_rsv_size;
497 |
498 | FDT_RW_PROBE(fdt);
499 |
500 | mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
501 | * sizeof(struct fdt_reserve_entry);
502 | fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
503 | fdt_size_dt_strings(fdt));
504 | fdt_set_totalsize(fdt, fdt_data_size_(fdt));
505 |
506 | return 0;
507 | }
508 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_strerror.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6 | */
7 | #include "libfdt_env.h"
8 |
9 | #include
10 | #include
11 |
12 | #include "libfdt_internal.h"
13 |
14 | struct fdt_errtabent {
15 | const char *str;
16 | };
17 |
18 | #define FDT_ERRTABENT(val) \
19 | [(val)] = { .str = #val, }
20 |
21 | static struct fdt_errtabent fdt_errtable[] = {
22 | FDT_ERRTABENT(FDT_ERR_NOTFOUND),
23 | FDT_ERRTABENT(FDT_ERR_EXISTS),
24 | FDT_ERRTABENT(FDT_ERR_NOSPACE),
25 |
26 | FDT_ERRTABENT(FDT_ERR_BADOFFSET),
27 | FDT_ERRTABENT(FDT_ERR_BADPATH),
28 | FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
29 | FDT_ERRTABENT(FDT_ERR_BADSTATE),
30 |
31 | FDT_ERRTABENT(FDT_ERR_TRUNCATED),
32 | FDT_ERRTABENT(FDT_ERR_BADMAGIC),
33 | FDT_ERRTABENT(FDT_ERR_BADVERSION),
34 | FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
35 | FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
36 | FDT_ERRTABENT(FDT_ERR_INTERNAL),
37 | FDT_ERRTABENT(FDT_ERR_BADNCELLS),
38 | FDT_ERRTABENT(FDT_ERR_BADVALUE),
39 | FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
40 | FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
41 | FDT_ERRTABENT(FDT_ERR_BADFLAGS),
42 | FDT_ERRTABENT(FDT_ERR_ALIGNMENT),
43 | };
44 | #define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
45 |
46 | const char *fdt_strerror(int errval)
47 | {
48 | if (errval > 0)
49 | return "";
50 | else if (errval == 0)
51 | return "";
52 | else if (-errval < FDT_ERRTABSIZE) {
53 | const char *s = fdt_errtable[-errval].str;
54 |
55 | if (s)
56 | return s;
57 | }
58 |
59 | return "";
60 | }
61 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_sw.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | static int fdt_sw_probe_(void *fdt)
14 | {
15 | if (!can_assume(VALID_INPUT)) {
16 | if (fdt_magic(fdt) == FDT_MAGIC)
17 | return -FDT_ERR_BADSTATE;
18 | else if (fdt_magic(fdt) != FDT_SW_MAGIC)
19 | return -FDT_ERR_BADMAGIC;
20 | }
21 |
22 | return 0;
23 | }
24 |
25 | #define FDT_SW_PROBE(fdt) \
26 | { \
27 | int err; \
28 | if ((err = fdt_sw_probe_(fdt)) != 0) \
29 | return err; \
30 | }
31 |
32 | /* 'memrsv' state: Initial state after fdt_create()
33 | *
34 | * Allowed functions:
35 | * fdt_add_reservemap_entry()
36 | * fdt_finish_reservemap() [moves to 'struct' state]
37 | */
38 | static int fdt_sw_probe_memrsv_(void *fdt)
39 | {
40 | int err = fdt_sw_probe_(fdt);
41 | if (err)
42 | return err;
43 |
44 | if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
45 | return -FDT_ERR_BADSTATE;
46 | return 0;
47 | }
48 |
49 | #define FDT_SW_PROBE_MEMRSV(fdt) \
50 | { \
51 | int err; \
52 | if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
53 | return err; \
54 | }
55 |
56 | /* 'struct' state: Enter this state after fdt_finish_reservemap()
57 | *
58 | * Allowed functions:
59 | * fdt_begin_node()
60 | * fdt_end_node()
61 | * fdt_property*()
62 | * fdt_finish() [moves to 'complete' state]
63 | */
64 | static int fdt_sw_probe_struct_(void *fdt)
65 | {
66 | int err = fdt_sw_probe_(fdt);
67 | if (err)
68 | return err;
69 |
70 | if (!can_assume(VALID_INPUT) &&
71 | fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
72 | return -FDT_ERR_BADSTATE;
73 | return 0;
74 | }
75 |
76 | #define FDT_SW_PROBE_STRUCT(fdt) \
77 | { \
78 | int err; \
79 | if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
80 | return err; \
81 | }
82 |
83 | static inline uint32_t sw_flags(void *fdt)
84 | {
85 | /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
86 | return fdt_last_comp_version(fdt);
87 | }
88 |
89 | /* 'complete' state: Enter this state after fdt_finish()
90 | *
91 | * Allowed functions: none
92 | */
93 |
94 | static void *fdt_grab_space_(void *fdt, size_t len)
95 | {
96 | unsigned int offset = fdt_size_dt_struct(fdt);
97 | unsigned int spaceleft;
98 |
99 | spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
100 | - fdt_size_dt_strings(fdt);
101 |
102 | if ((offset + len < offset) || (offset + len > spaceleft))
103 | return NULL;
104 |
105 | fdt_set_size_dt_struct(fdt, offset + len);
106 | return fdt_offset_ptr_w_(fdt, offset);
107 | }
108 |
109 | int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
110 | {
111 | const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
112 | sizeof(struct fdt_reserve_entry));
113 | void *fdt = buf;
114 |
115 | if (bufsize < hdrsize)
116 | return -FDT_ERR_NOSPACE;
117 |
118 | if (flags & ~FDT_CREATE_FLAGS_ALL)
119 | return -FDT_ERR_BADFLAGS;
120 |
121 | memset(buf, 0, bufsize);
122 |
123 | /*
124 | * magic and last_comp_version keep intermediate state during the fdt
125 | * creation process, which is replaced with the proper FDT format by
126 | * fdt_finish().
127 | *
128 | * flags should be accessed with sw_flags().
129 | */
130 | fdt_set_magic(fdt, FDT_SW_MAGIC);
131 | fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
132 | fdt_set_last_comp_version(fdt, flags);
133 |
134 | fdt_set_totalsize(fdt, bufsize);
135 |
136 | fdt_set_off_mem_rsvmap(fdt, hdrsize);
137 | fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
138 | fdt_set_off_dt_strings(fdt, 0);
139 |
140 | return 0;
141 | }
142 |
143 | int fdt_create(void *buf, int bufsize)
144 | {
145 | return fdt_create_with_flags(buf, bufsize, 0);
146 | }
147 |
148 | int fdt_resize(void *fdt, void *buf, int bufsize)
149 | {
150 | size_t headsize, tailsize;
151 | char *oldtail, *newtail;
152 |
153 | FDT_SW_PROBE(fdt);
154 |
155 | if (bufsize < 0)
156 | return -FDT_ERR_NOSPACE;
157 |
158 | headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
159 | tailsize = fdt_size_dt_strings(fdt);
160 |
161 | if (!can_assume(VALID_DTB) &&
162 | headsize + tailsize > fdt_totalsize(fdt))
163 | return -FDT_ERR_INTERNAL;
164 |
165 | if ((headsize + tailsize) > (unsigned)bufsize)
166 | return -FDT_ERR_NOSPACE;
167 |
168 | oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
169 | newtail = (char *)buf + bufsize - tailsize;
170 |
171 | /* Two cases to avoid clobbering data if the old and new
172 | * buffers partially overlap */
173 | if (buf <= fdt) {
174 | memmove(buf, fdt, headsize);
175 | memmove(newtail, oldtail, tailsize);
176 | } else {
177 | memmove(newtail, oldtail, tailsize);
178 | memmove(buf, fdt, headsize);
179 | }
180 |
181 | fdt_set_totalsize(buf, bufsize);
182 | if (fdt_off_dt_strings(buf))
183 | fdt_set_off_dt_strings(buf, bufsize);
184 |
185 | return 0;
186 | }
187 |
188 | int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
189 | {
190 | struct fdt_reserve_entry *re;
191 | int offset;
192 |
193 | FDT_SW_PROBE_MEMRSV(fdt);
194 |
195 | offset = fdt_off_dt_struct(fdt);
196 | if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
197 | return -FDT_ERR_NOSPACE;
198 |
199 | re = (struct fdt_reserve_entry *)((char *)fdt + offset);
200 | re->address = cpu_to_fdt64(addr);
201 | re->size = cpu_to_fdt64(size);
202 |
203 | fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
204 |
205 | return 0;
206 | }
207 |
208 | int fdt_finish_reservemap(void *fdt)
209 | {
210 | int err = fdt_add_reservemap_entry(fdt, 0, 0);
211 |
212 | if (err)
213 | return err;
214 |
215 | fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
216 | return 0;
217 | }
218 |
219 | int fdt_begin_node(void *fdt, const char *name)
220 | {
221 | struct fdt_node_header *nh;
222 | int namelen;
223 |
224 | FDT_SW_PROBE_STRUCT(fdt);
225 |
226 | namelen = strlen(name) + 1;
227 | nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
228 | if (! nh)
229 | return -FDT_ERR_NOSPACE;
230 |
231 | nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
232 | memcpy(nh->name, name, namelen);
233 | return 0;
234 | }
235 |
236 | int fdt_end_node(void *fdt)
237 | {
238 | fdt32_t *en;
239 |
240 | FDT_SW_PROBE_STRUCT(fdt);
241 |
242 | en = fdt_grab_space_(fdt, FDT_TAGSIZE);
243 | if (! en)
244 | return -FDT_ERR_NOSPACE;
245 |
246 | *en = cpu_to_fdt32(FDT_END_NODE);
247 | return 0;
248 | }
249 |
250 | static int fdt_add_string_(void *fdt, const char *s)
251 | {
252 | char *strtab = (char *)fdt + fdt_totalsize(fdt);
253 | unsigned int strtabsize = fdt_size_dt_strings(fdt);
254 | unsigned int len = strlen(s) + 1;
255 | unsigned int struct_top, offset;
256 |
257 | offset = strtabsize + len;
258 | struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
259 | if (fdt_totalsize(fdt) - offset < struct_top)
260 | return 0; /* no more room :( */
261 |
262 | memcpy(strtab - offset, s, len);
263 | fdt_set_size_dt_strings(fdt, strtabsize + len);
264 | return -offset;
265 | }
266 |
267 | /* Must only be used to roll back in case of error */
268 | static void fdt_del_last_string_(void *fdt, const char *s)
269 | {
270 | int strtabsize = fdt_size_dt_strings(fdt);
271 | int len = strlen(s) + 1;
272 |
273 | fdt_set_size_dt_strings(fdt, strtabsize - len);
274 | }
275 |
276 | static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
277 | {
278 | char *strtab = (char *)fdt + fdt_totalsize(fdt);
279 | int strtabsize = fdt_size_dt_strings(fdt);
280 | const char *p;
281 |
282 | *allocated = 0;
283 |
284 | p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
285 | if (p)
286 | return p - strtab;
287 |
288 | *allocated = 1;
289 |
290 | return fdt_add_string_(fdt, s);
291 | }
292 |
293 | int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
294 | {
295 | struct fdt_property *prop;
296 | int nameoff;
297 | int allocated;
298 |
299 | FDT_SW_PROBE_STRUCT(fdt);
300 |
301 | /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
302 | if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
303 | allocated = 1;
304 | nameoff = fdt_add_string_(fdt, name);
305 | } else {
306 | nameoff = fdt_find_add_string_(fdt, name, &allocated);
307 | }
308 | if (nameoff == 0)
309 | return -FDT_ERR_NOSPACE;
310 |
311 | prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
312 | if (! prop) {
313 | if (allocated)
314 | fdt_del_last_string_(fdt, name);
315 | return -FDT_ERR_NOSPACE;
316 | }
317 |
318 | prop->tag = cpu_to_fdt32(FDT_PROP);
319 | prop->nameoff = cpu_to_fdt32(nameoff);
320 | prop->len = cpu_to_fdt32(len);
321 | *valp = prop->data;
322 | return 0;
323 | }
324 |
325 | int fdt_property(void *fdt, const char *name, const void *val, int len)
326 | {
327 | void *ptr;
328 | int ret;
329 |
330 | ret = fdt_property_placeholder(fdt, name, len, &ptr);
331 | if (ret)
332 | return ret;
333 | memcpy(ptr, val, len);
334 | return 0;
335 | }
336 |
337 | int fdt_finish(void *fdt)
338 | {
339 | char *p = (char *)fdt;
340 | fdt32_t *end;
341 | int oldstroffset, newstroffset;
342 | uint32_t tag;
343 | int offset, nextoffset;
344 |
345 | FDT_SW_PROBE_STRUCT(fdt);
346 |
347 | /* Add terminator */
348 | end = fdt_grab_space_(fdt, sizeof(*end));
349 | if (! end)
350 | return -FDT_ERR_NOSPACE;
351 | *end = cpu_to_fdt32(FDT_END);
352 |
353 | /* Relocate the string table */
354 | oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
355 | newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
356 | memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
357 | fdt_set_off_dt_strings(fdt, newstroffset);
358 |
359 | /* Walk the structure, correcting string offsets */
360 | offset = 0;
361 | while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
362 | if (tag == FDT_PROP) {
363 | struct fdt_property *prop =
364 | fdt_offset_ptr_w_(fdt, offset);
365 | int nameoff;
366 |
367 | nameoff = fdt32_to_cpu(prop->nameoff);
368 | nameoff += fdt_size_dt_strings(fdt);
369 | prop->nameoff = cpu_to_fdt32(nameoff);
370 | }
371 | offset = nextoffset;
372 | }
373 | if (nextoffset < 0)
374 | return nextoffset;
375 |
376 | /* Finally, adjust the header */
377 | fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
378 |
379 | /* And fix up fields that were keeping intermediate state. */
380 | fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
381 | fdt_set_magic(fdt, FDT_MAGIC);
382 |
383 | return 0;
384 | }
385 |
--------------------------------------------------------------------------------
/lib/libfdt/fdt_wip.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 | /*
3 | * libfdt - Flat Device Tree manipulation
4 | * Copyright (C) 2006 David Gibson, IBM Corporation.
5 | */
6 | #include "libfdt_env.h"
7 |
8 | #include
9 | #include
10 |
11 | #include "libfdt_internal.h"
12 |
13 | int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
14 | const char *name, int namelen,
15 | uint32_t idx, const void *val,
16 | int len)
17 | {
18 | void *propval;
19 | int proplen;
20 |
21 | propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
22 | &proplen);
23 | if (!propval)
24 | return proplen;
25 |
26 | if ((unsigned)proplen < (len + idx))
27 | return -FDT_ERR_NOSPACE;
28 |
29 | memcpy((char *)propval + idx, val, len);
30 | return 0;
31 | }
32 |
33 | int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
34 | const void *val, int len)
35 | {
36 | const void *propval;
37 | int proplen;
38 |
39 | propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
40 | if (!propval)
41 | return proplen;
42 |
43 | if (proplen != len)
44 | return -FDT_ERR_NOSPACE;
45 |
46 | return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
47 | strlen(name), 0,
48 | val, len);
49 | }
50 |
51 | static void fdt_nop_region_(void *start, int len)
52 | {
53 | fdt32_t *p;
54 |
55 | for (p = start; (char *)p < ((char *)start + len); p++)
56 | *p = cpu_to_fdt32(FDT_NOP);
57 | }
58 |
59 | int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
60 | {
61 | struct fdt_property *prop;
62 | int len;
63 |
64 | prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
65 | if (!prop)
66 | return len;
67 |
68 | fdt_nop_region_(prop, len + sizeof(*prop));
69 |
70 | return 0;
71 | }
72 |
73 | int fdt_node_end_offset_(void *fdt, int offset)
74 | {
75 | int depth = 0;
76 |
77 | while ((offset >= 0) && (depth >= 0))
78 | offset = fdt_next_node(fdt, offset, &depth);
79 |
80 | return offset;
81 | }
82 |
83 | int fdt_nop_node(void *fdt, int nodeoffset)
84 | {
85 | int endoffset;
86 |
87 | endoffset = fdt_node_end_offset_(fdt, nodeoffset);
88 | if (endoffset < 0)
89 | return endoffset;
90 |
91 | fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0),
92 | endoffset - nodeoffset);
93 | return 0;
94 | }
95 |
--------------------------------------------------------------------------------
/lib/libfdt/libfdt_env.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
2 | #ifndef LIBFDT_ENV_H
3 | #define LIBFDT_ENV_H
4 | /*
5 | * libfdt - Flat Device Tree manipulation
6 | * Copyright (C) 2006 David Gibson, IBM Corporation.
7 | * Copyright 2012 Kim Phillips, Freescale Semiconductor.
8 | */
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #ifdef __CHECKER__
18 | #define FDT_FORCE __attribute__((force))
19 | #define FDT_BITWISE __attribute__((bitwise))
20 | #else
21 | #define FDT_FORCE
22 | #define FDT_BITWISE
23 | #endif
24 |
25 | typedef uint16_t FDT_BITWISE fdt16_t;
26 | typedef uint32_t FDT_BITWISE fdt32_t;
27 | typedef uint64_t FDT_BITWISE fdt64_t;
28 |
29 | #define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
30 | #define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
31 | #define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
32 | (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
33 | #define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
34 | (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
35 | (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
36 | (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
37 |
38 | static inline uint16_t fdt16_to_cpu(fdt16_t x)
39 | {
40 | return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
41 | }
42 | static inline fdt16_t cpu_to_fdt16(uint16_t x)
43 | {
44 | return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
45 | }
46 |
47 | static inline uint32_t fdt32_to_cpu(fdt32_t x)
48 | {
49 | return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
50 | }
51 | static inline fdt32_t cpu_to_fdt32(uint32_t x)
52 | {
53 | return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
54 | }
55 |
56 | static inline uint64_t fdt64_to_cpu(fdt64_t x)
57 | {
58 | return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
59 | }
60 | static inline fdt64_t cpu_to_fdt64(uint64_t x)
61 | {
62 | return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
63 | }
64 | #undef CPU_TO_FDT64
65 | #undef CPU_TO_FDT32
66 | #undef CPU_TO_FDT16
67 | #undef EXTRACT_BYTE
68 |
69 | #ifdef __APPLE__
70 | #include
71 |
72 | /* strnlen() is not available on Mac OS < 10.7 */
73 | # if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \
74 | MAC_OS_X_VERSION_10_7)
75 |
76 | #define strnlen fdt_strnlen
77 |
78 | /*
79 | * fdt_strnlen: returns the length of a string or max_count - which ever is
80 | * smallest.
81 | * Input 1 string: the string whose size is to be determined
82 | * Input 2 max_count: the maximum value returned by this function
83 | * Output: length of the string or max_count (the smallest of the two)
84 | */
85 | static inline size_t fdt_strnlen(const char *string, size_t max_count)
86 | {
87 | const char *p = memchr(string, 0, max_count);
88 | return p ? p - string : max_count;
89 | }
90 |
91 | #endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED <
92 | MAC_OS_X_VERSION_10_7) */
93 |
94 | #endif /* __APPLE__ */
95 |
96 | #endif /* LIBFDT_ENV_H */
97 |
--------------------------------------------------------------------------------
/lib/libfdt/libfdt_internal.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
2 | #ifndef LIBFDT_INTERNAL_H
3 | #define LIBFDT_INTERNAL_H
4 | /*
5 | * libfdt - Flat Device Tree manipulation
6 | * Copyright (C) 2006 David Gibson, IBM Corporation.
7 | */
8 | #include
9 | #include
10 |
11 | #define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
12 | #define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
13 |
14 | int32_t fdt_ro_probe_(const void *fdt);
15 | #define FDT_RO_PROBE(fdt) \
16 | { \
17 | int32_t totalsize_; \
18 | if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
19 | return totalsize_; \
20 | }
21 |
22 | int fdt_check_node_offset_(const void *fdt, int offset);
23 | int fdt_check_prop_offset_(const void *fdt, int offset);
24 |
25 | const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
26 | int s_len);
27 | static inline const char *fdt_find_string_(const char *strtab, int tabsize,
28 | const char *s)
29 | {
30 | return fdt_find_string_len_(strtab, tabsize, s, strlen(s));
31 | }
32 |
33 | int fdt_node_end_offset_(void *fdt, int nodeoffset);
34 |
35 | static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
36 | {
37 | return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
38 | }
39 |
40 | static inline void *fdt_offset_ptr_w_(void *fdt, int offset)
41 | {
42 | return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset);
43 | }
44 |
45 | static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n)
46 | {
47 | const struct fdt_reserve_entry *rsv_table =
48 | (const struct fdt_reserve_entry *)
49 | ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
50 |
51 | return rsv_table + n;
52 | }
53 | static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
54 | {
55 | return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n);
56 | }
57 |
58 | /*
59 | * Internal helpers to access structural elements of the device tree
60 | * blob (rather than for example reading integers from within property
61 | * values). We assume that we are either given a naturally aligned
62 | * address for the platform or if we are not, we are on a platform
63 | * where unaligned memory reads will be handled in a graceful manner.
64 | * If not the external helpers fdtXX_ld() from libfdt.h can be used
65 | * instead.
66 | */
67 | static inline uint32_t fdt32_ld_(const fdt32_t *p)
68 | {
69 | return fdt32_to_cpu(*p);
70 | }
71 |
72 | static inline uint64_t fdt64_ld_(const fdt64_t *p)
73 | {
74 | return fdt64_to_cpu(*p);
75 | }
76 |
77 | #define FDT_SW_MAGIC (~FDT_MAGIC)
78 |
79 | /**********************************************************************/
80 | /* Checking controls */
81 | /**********************************************************************/
82 |
83 | #ifndef FDT_ASSUME_MASK
84 | #define FDT_ASSUME_MASK 0
85 | #endif
86 |
87 | /*
88 | * Defines assumptions which can be enabled. Each of these can be enabled
89 | * individually. For maximum safety, don't enable any assumptions!
90 | *
91 | * For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
92 | * You should have another method of validating the device tree, such as a
93 | * signature or hash check before using libfdt.
94 | *
95 | * For situations where security is not a concern it may be safe to enable
96 | * ASSUME_SANE.
97 | */
98 | enum {
99 | /*
100 | * This does essentially no checks. Only the latest device-tree
101 | * version is correctly handled. Inconsistencies or errors in the device
102 | * tree may cause undefined behaviour or crashes. Invalid parameters
103 | * passed to libfdt may do the same.
104 | *
105 | * If an error occurs when modifying the tree it may leave the tree in
106 | * an intermediate (but valid) state. As an example, adding a property
107 | * where there is insufficient space may result in the property name
108 | * being added to the string table even though the property itself is
109 | * not added to the struct section.
110 | *
111 | * Only use this if you have a fully validated device tree with
112 | * the latest supported version and wish to minimise code size.
113 | */
114 | ASSUME_PERFECT = 0xff,
115 |
116 | /*
117 | * This assumes that the device tree is sane. i.e. header metadata
118 | * and basic hierarchy are correct.
119 | *
120 | * With this assumption enabled, normal device trees produced by libfdt
121 | * and the compiler should be handled safely. Malicious device trees and
122 | * complete garbage may cause libfdt to behave badly or crash. Truncated
123 | * device trees (e.g. those only partially loaded) can also cause
124 | * problems.
125 | *
126 | * Note: Only checks that relate exclusively to the device tree itself
127 | * (not the parameters passed to libfdt) are disabled by this
128 | * assumption. This includes checking headers, tags and the like.
129 | */
130 | ASSUME_VALID_DTB = 1 << 0,
131 |
132 | /*
133 | * This builds on ASSUME_VALID_DTB and further assumes that libfdt
134 | * functions are called with valid parameters, i.e. not trigger
135 | * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any
136 | * extensive checking of parameters and the device tree, making various
137 | * assumptions about correctness.
138 | *
139 | * It doesn't make sense to enable this assumption unless
140 | * ASSUME_VALID_DTB is also enabled.
141 | */
142 | ASSUME_VALID_INPUT = 1 << 1,
143 |
144 | /*
145 | * This disables checks for device-tree version and removes all code
146 | * which handles older versions.
147 | *
148 | * Only enable this if you know you have a device tree with the latest
149 | * version.
150 | */
151 | ASSUME_LATEST = 1 << 2,
152 |
153 | /*
154 | * This assumes that it is OK for a failed addition to the device tree,
155 | * due to lack of space or some other problem, to skip any rollback
156 | * steps (such as dropping the property name from the string table).
157 | * This is safe to enable in most circumstances, even though it may
158 | * leave the tree in a sub-optimal state.
159 | */
160 | ASSUME_NO_ROLLBACK = 1 << 3,
161 |
162 | /*
163 | * This assumes that the device tree components appear in a 'convenient'
164 | * order, i.e. the memory reservation block first, then the structure
165 | * block and finally the string block.
166 | *
167 | * This order is not specified by the device-tree specification,
168 | * but is expected by libfdt. The device-tree compiler always created
169 | * device trees with this order.
170 | *
171 | * This assumption disables a check in fdt_open_into() and removes the
172 | * ability to fix the problem there. This is safe if you know that the
173 | * device tree is correctly ordered. See fdt_blocks_misordered_().
174 | */
175 | ASSUME_LIBFDT_ORDER = 1 << 4,
176 |
177 | /*
178 | * This assumes that libfdt itself does not have any internal bugs. It
179 | * drops certain checks that should never be needed unless libfdt has an
180 | * undiscovered bug.
181 | *
182 | * This can generally be considered safe to enable.
183 | */
184 | ASSUME_LIBFDT_FLAWLESS = 1 << 5,
185 | };
186 |
187 | /**
188 | * can_assume_() - check if a particular assumption is enabled
189 | *
190 | * @mask: Mask to check (ASSUME_...)
191 | * @return true if that assumption is enabled, else false
192 | */
193 | static inline bool can_assume_(int mask)
194 | {
195 | return FDT_ASSUME_MASK & mask;
196 | }
197 |
198 | /** helper macros for checking assumptions */
199 | #define can_assume(_assume) can_assume_(ASSUME_ ## _assume)
200 |
201 | #endif /* LIBFDT_INTERNAL_H */
202 |
--------------------------------------------------------------------------------
/lib/libfdt/meson.build:
--------------------------------------------------------------------------------
1 | version_script = '-Wl,--version-script=@0@'.format(meson.current_source_dir() / 'version.lds')
2 | if not cc.has_link_argument(version_script)
3 | version_script = []
4 | endif
5 |
6 | sources = files(
7 | 'fdt.c',
8 | 'fdt_addresses.c',
9 | 'fdt_check.c',
10 | 'fdt_empty_tree.c',
11 | 'fdt_overlay.c',
12 | 'fdt_ro.c',
13 | 'fdt_rw.c',
14 | 'fdt_strerror.c',
15 | 'fdt_sw.c',
16 | 'fdt_wip.c',
17 | )
18 |
19 | link_args = []
20 | if cc.has_link_argument('-Wl,--no-undefined')
21 | link_args += '-Wl,--no-undefined'
22 | else
23 | # -undefined error is the equivalent of --no-undefined for the macOS linker,
24 | # but -undefined would also be understood as a valid argument for GNU ld!
25 | link_args += cc.get_supported_link_arguments('-Wl,-undefined,error')
26 | endif
27 |
28 | link_args += version_script
29 | libfdt = library(
30 | 'fdt', sources,
31 | version: meson.project_version(),
32 | link_args: link_args,
33 | link_depends: 'version.lds',
34 | install: true,
35 | )
36 |
37 | libfdt_inc = include_directories('.')
38 |
39 | libfdt_dep = declare_dependency(
40 | include_directories: libfdt_inc,
41 | link_with: libfdt,
42 | )
43 |
44 | install_headers(
45 | files(
46 | 'fdt.h',
47 | 'libfdt.h',
48 | 'libfdt_env.h',
49 | )
50 | )
51 |
52 | pkgconfig = import('pkgconfig')
53 |
54 | pkgconfig.generate(
55 | libraries: libfdt,
56 | version: meson.project_version(),
57 | filebase: 'libfdt',
58 | name: 'libfdt',
59 | description: 'Flat Device Tree manipulation',
60 | )
61 |
--------------------------------------------------------------------------------
/lib/libfdt/sbom.cdx.json:
--------------------------------------------------------------------------------
1 | {
2 | "bomFormat": "CycloneDX",
3 | "specVersion": "1.6",
4 | "version": 1,
5 | "metadata": {
6 | "authors": [
7 | {
8 | "name": "@VCS_SBOM_AUTHORS@"
9 | }
10 | ]
11 | },
12 | "components": [
13 | {
14 | "type": "library",
15 | "bom-ref": "pkg:github/dgibson/libfdt@@VCS_TAG@",
16 | "cpe": "cpe:2.3:a:dgibson:libfdt:@VCS_TAG@:*:*:*:*:*:*:*",
17 | "name": "libfdt",
18 | "version": "@VCS_VERSION@",
19 | "description": "Utility library for reading and manipulating the FDT binary format",
20 | "supplier": {
21 | "name": "libfdt developers"
22 | },
23 | "licenses": [
24 | {
25 | "license": {
26 | "id": "BSD-2-Clause"
27 | }
28 | },
29 | {
30 | "license": {
31 | "id": "GPL-2.0-or-later"
32 | }
33 | }
34 | ],
35 | "externalReferences": [
36 | {
37 | "type": "vcs",
38 | "url": "https://github.com/dgibson/dtc"
39 | }
40 | ]
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/lib/libfdt/version.lds:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
2 | LIBFDT_1.2 {
3 | global:
4 | fdt_next_node;
5 | fdt_check_header;
6 | fdt_move;
7 | fdt_string;
8 | fdt_num_mem_rsv;
9 | fdt_get_mem_rsv;
10 | fdt_subnode_offset_namelen;
11 | fdt_subnode_offset;
12 | fdt_path_offset_namelen;
13 | fdt_path_offset;
14 | fdt_get_name;
15 | fdt_get_property_namelen;
16 | fdt_get_property;
17 | fdt_getprop_namelen;
18 | fdt_getprop;
19 | fdt_get_phandle;
20 | fdt_get_alias_namelen;
21 | fdt_get_alias;
22 | fdt_get_path;
23 | fdt_header_size;
24 | fdt_supernode_atdepth_offset;
25 | fdt_node_depth;
26 | fdt_parent_offset;
27 | fdt_node_offset_by_prop_value;
28 | fdt_node_offset_by_phandle;
29 | fdt_node_check_compatible;
30 | fdt_node_offset_by_compatible;
31 | fdt_setprop_inplace;
32 | fdt_nop_property;
33 | fdt_nop_node;
34 | fdt_create;
35 | fdt_add_reservemap_entry;
36 | fdt_finish_reservemap;
37 | fdt_begin_node;
38 | fdt_property;
39 | fdt_end_node;
40 | fdt_finish;
41 | fdt_open_into;
42 | fdt_pack;
43 | fdt_add_mem_rsv;
44 | fdt_del_mem_rsv;
45 | fdt_set_name;
46 | fdt_setprop_namelen;
47 | fdt_setprop;
48 | fdt_delprop;
49 | fdt_add_subnode_namelen;
50 | fdt_add_subnode;
51 | fdt_del_node;
52 | fdt_strerror;
53 | fdt_offset_ptr;
54 | fdt_next_tag;
55 | fdt_appendprop;
56 | fdt_create_empty_tree;
57 | fdt_first_property_offset;
58 | fdt_get_property_by_offset;
59 | fdt_getprop_by_offset;
60 | fdt_next_property_offset;
61 | fdt_first_subnode;
62 | fdt_next_subnode;
63 | fdt_address_cells;
64 | fdt_size_cells;
65 | fdt_stringlist_contains;
66 | fdt_stringlist_count;
67 | fdt_stringlist_search;
68 | fdt_stringlist_get;
69 | fdt_resize;
70 | fdt_overlay_apply;
71 | fdt_get_string;
72 | fdt_find_max_phandle;
73 | fdt_generate_phandle;
74 | fdt_check_full;
75 | fdt_setprop_placeholder_namelen;
76 | fdt_setprop_placeholder;
77 | fdt_property_placeholder;
78 | fdt_header_size_;
79 | fdt_appendprop_addrrange;
80 | fdt_setprop_inplace_namelen_partial;
81 | fdt_create_with_flags;
82 | fdt_overlay_target_offset;
83 | fdt_get_symbol;
84 | fdt_get_symbol_namelen;
85 | local:
86 | *;
87 | };
88 |
--------------------------------------------------------------------------------
/npcm7xx/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | CROSS_COMPILE ?= arm-none-eabi-
16 |
17 | CC = $(CROSS_COMPILE)gcc
18 | OBJCOPY = $(CROSS_COMPILE)objcopy
19 |
20 | CFLAGS = -Os -g -mcpu=cortex-a9
21 | ASFLAGS = $(CFLAGS) -Wa,-mcpu=cortex-a9+mp
22 | LDSCRIPT = bootrom.ld
23 | LDFLAGS = -Wl,--build-id=none -static -nostdlib -T $(LDSCRIPT)
24 |
25 | OBJS := start.o image.o
26 |
27 | .PHONY: all clean
28 | all: npcm7xx_bootrom.bin
29 |
30 | clean:
31 | rm -f *.o *.bin *.elf
32 |
33 | npcm7xx_bootrom.bin: npcm7xx_bootrom.elf
34 | $(OBJCOPY) -O binary $< $@
35 |
36 | npcm7xx_bootrom.elf: $(OBJS) $(LDSCRIPT)
37 | $(CC) -o $@ $(LDFLAGS) $(OBJS)
38 |
--------------------------------------------------------------------------------
/npcm7xx/README.md:
--------------------------------------------------------------------------------
1 | # Virtual Boot ROM for NPCM7xx SoCs
2 |
3 | This is not an officially supported Google product.
4 |
5 | This is a super simple Boot ROM that is intended to be used as a `-bios` image
6 | for [QEMU](http://www.qemu.org/) when emulating an NPCM7xx-based machine.
7 |
8 | ## Building
9 |
10 | If you have a 32-bit ARM compiler installed as `arm-none-eabi-gcc`, simply run
11 | `make`.
12 |
13 | If your ARM compiler has a different name, you'll need to override the
14 | `CROSS_COMPILE` prefix, e.g. like this:
15 |
16 | ```
17 | make CROSS_COMPILE=arm-linux-gnueabi-
18 | ```
19 |
20 | If either case is successful, a `npcm7xx_bootrom.bin` file will be produced.
21 |
22 | ## Using
23 |
24 | The Boot ROM image may be passed to a QEMU system emulator using the `-bios` option. For example like this:
25 |
26 | ```
27 | qemu-system-arm -machine quanta-gsj -nographic \
28 | -bios "${IMAGES}/npcm7xx_bootrom.bin"
29 | -drive file="${IMAGES}/image-bmc,if=mtd,bus=0,unit=0,format=raw,snapshot=on"
30 | ```
31 |
32 | ## Limitations
33 |
34 | * Secure boot is not supported.
35 | * Only booting from offset 0 of the flash at SPI0 CS0 is implemented.
36 | * Fallback images (if the first image doesn't boot) are not implemented.
37 | * Exception vectors are copied to SRAM, but not remapped.
38 | * Most OTP bits and straps are not honored.
39 | * The reset type bits are not updated.
40 | * OTP protection is not implemented.
41 | * No clock initialization is performed.
42 | * UART programming protocol is not implemented.
43 | * Host notification through the PCI mailbox is not implemented.
44 | * Most fields in the ROM status structure are not set.
45 |
--------------------------------------------------------------------------------
/npcm7xx/bootrom.ld:
--------------------------------------------------------------------------------
1 | /*
2 | * Linker script for the Boot ROM.
3 | *
4 | * Copyright 2020 Google LLC
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | MEMORY
20 | {
21 | rom (rx) : ORIGIN = 0xFFFF0000, LENGTH = 64K
22 | ram (a!rx) : ORIGIN = 0xFFFD0000, LENGTH = 128K
23 | }
24 |
25 | SECTIONS
26 | {
27 | /* Vectors are loaded into ROM, and copied into SRAM. */
28 | .text.vectors : {
29 | *(.text.vectors)
30 | . = 0x100;
31 | } >ram AT>rom
32 | /* The rest of the code follows the vectors, but is not copied. */
33 | .text : {
34 | *(.text .text.*)
35 | *(.rodata .rodata.*)
36 | . = ALIGN(32);
37 | _etext = .;
38 | } >rom
39 | /*
40 | * Data follows the code in ROM, and is copied after the vectors in RAM.
41 | * 32-byte aligned so we can use simple and fast copy loops.
42 | */
43 | .data : {
44 | _data = .;
45 | *(.data.rom_status)
46 | *(.data .data.*)
47 | . = ALIGN(32);
48 | _edata = .;
49 | } >ram AT>rom
50 | /* BSS lives in RAM, after the data section. */
51 | .bss : {
52 | *(.bss .bss.*)
53 | . = ALIGN(32);
54 | _end = .;
55 | } >ram
56 | }
57 |
--------------------------------------------------------------------------------
/npcm7xx/image.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Boot image parsing and loading.
3 | *
4 | * Copyright 2020 Google LLC
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | #include
20 |
21 | #define SPI0CS0 0x80000000
22 | #define GCR 0xf0800000
23 | #define CLK 0xf0801000
24 | #define FIU0 0xfb000000
25 |
26 | #define CLK_CLKDIV3 0x58
27 |
28 | #define FIU_DRD_CFG 0x00
29 | #define FIU_CFG 0x78
30 |
31 | #define BOOT_MAGIC0 0x000
32 | #define BOOT_MAGIC1 0x004
33 | #define BOOT_FIU_DRD_CFG 0x108
34 | #define BOOT_FIU_CLK_DIV 0x10c
35 | #define BOOT_DEST_ADDR 0x140
36 | #define BOOT_CODE_SIZE 0x144
37 | #define BOOT_VERSION 0x148
38 | #define BOOT_CODE_OFFSET 0x200
39 |
40 | #define BOOT_MAGIC0_VALUE 0xaa550750
41 | #define BOOT_MAGIC1_VALUE 0x424f4f54
42 |
43 | /*
44 | * This structure must reside at offset 0x100 in SRAM.
45 | *
46 | * See the Check_ROMCode_Status function in the Nuvoton bootblock:
47 | * https://github.com/Nuvoton-Israel/bootblock/blob/master/Src/bootblock_main.c#L795
48 | */
49 | struct rom_status {
50 | uint8_t reserved[12];
51 | uint8_t start_tag[8];
52 | uint32_t status;
53 | } rom_status __attribute__((section(".data.rom_status"))) = {
54 | .status = 0x21, /* SPI0 CS0 offset 0 */
55 | };
56 |
57 | extern void panic(const char *);
58 |
59 | static void reg_write(uintptr_t base, uintptr_t offset, uint32_t value)
60 | {
61 | asm volatile("str %0, [%1, %2]"
62 | :
63 | : "r"(value), "r"(base), "i"(offset)
64 | : "memory");
65 | }
66 |
67 | static uint32_t image_read_u8(uintptr_t base, uintptr_t offset)
68 | {
69 | return *(uint8_t *)(base + offset);
70 | }
71 |
72 | static uint32_t image_read_u32(uintptr_t base, uintptr_t offset)
73 | {
74 | return *(uint32_t *)(base + offset);
75 | }
76 |
77 | void copy_boot_image(uintptr_t dest_addr, uintptr_t src_addr, int32_t len)
78 | {
79 | uint32_t *dst = (uint32_t *)dest_addr;
80 | uint32_t *src = (uint32_t *)src_addr;
81 |
82 | while (len > 0) {
83 | *dst++ = *src++;
84 | len -= sizeof(*dst);
85 | }
86 | }
87 |
88 | uintptr_t load_boot_image(void)
89 | {
90 | uintptr_t dest_addr;
91 | uint32_t drd_cfg;
92 | uint8_t clk_div;
93 |
94 | reg_write(FIU0, FIU_CFG, 0x0000000b);
95 |
96 | if (image_read_u32(SPI0CS0, BOOT_MAGIC0) != BOOT_MAGIC0_VALUE) {
97 | panic("Bad image magic0 value");
98 | }
99 | if (image_read_u32(SPI0CS0, BOOT_MAGIC1) != BOOT_MAGIC1_VALUE) {
100 | panic("Bad image magic1 value");
101 | }
102 |
103 | clk_div = image_read_u8(SPI0CS0, BOOT_FIU_CLK_DIV);
104 | if (clk_div != 0) {
105 | reg_write(FIU0, FIU_DRD_CFG, image_read_u32(SPI0CS0, BOOT_FIU_DRD_CFG));
106 | reg_write(CLK, CLK_CLKDIV3, clk_div << 6);
107 | }
108 |
109 | dest_addr = image_read_u32(SPI0CS0, BOOT_DEST_ADDR);
110 | if (dest_addr == 0) {
111 | return SPI0CS0 + 0x200;
112 | }
113 |
114 | copy_boot_image(dest_addr, SPI0CS0,
115 | image_read_u32(SPI0CS0, BOOT_CODE_SIZE) + 0x200);
116 |
117 | return dest_addr + 0x200;
118 | }
119 |
--------------------------------------------------------------------------------
/npcm7xx/start.S:
--------------------------------------------------------------------------------
1 | /*
2 | * Top-level entry points to the Boot ROM. This includes:
3 | * - Reset, exception and interrupt vectors.
4 | * - C run-time initialization.
5 | * - Secondary CPU boot code.
6 | *
7 | * Copyright 2020 Google LLC
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | #define KiB (1024)
23 |
24 | #define SRAM_SIZE (128 * KiB)
25 |
26 | .section .text.vectors, "ax"
27 |
28 | .global _start
29 | .type _start, %function
30 | _start:
31 | ldr pc, reset_addr
32 | . = 0x04
33 | b undefined_instruction
34 | . = 0x08
35 | b software_interrupt
36 | . = 0x0c
37 | b prefetch_abort
38 | . = 0x10
39 | b data_abort
40 | . = 0x18
41 | b interrupt
42 | . = 0x1c
43 | b fast_interrupt
44 |
45 | .align 2
46 | reset_addr:
47 | .word reset
48 |
49 | undefined_instruction:
50 | mov r0, #1
51 | ldr pc, handle_exception_addr
52 |
53 | software_interrupt:
54 | mov r0, #2
55 | ldr pc, handle_exception_addr
56 |
57 | prefetch_abort:
58 | mov r0, #3
59 | ldr pc, handle_exception_addr
60 |
61 | data_abort:
62 | mov r0, #4
63 | ldr pc, handle_exception_addr
64 |
65 | interrupt:
66 | mov r0, #6
67 | ldr pc, handle_exception_addr
68 |
69 | fast_interrupt:
70 | mov r0, #7
71 | ldr pc, handle_exception_addr
72 |
73 | handle_exception_addr:
74 | .word handle_exception
75 |
76 | vectors_end:
77 |
78 | . = 0xf8
79 | chip_id:
80 | .word 0x00a92750
81 |
82 | . = 0xfc
83 | rom_version:
84 | .word 0x00010055
85 |
86 | .text
87 | .align 2
88 | handle_exception:
89 |
90 | .global panic
91 | .type panic, %function
92 | panic:
93 | 1: wfi
94 | b 1b
95 | .size panic, . - panic
96 |
97 | .type reset, %function
98 | reset:
99 | mov r0, #0
100 | // Read the CPU ID from MPIDR.
101 | mrc p15, 0, r1, c0, c0, 5
102 | tst r1, #0x03
103 | beq cpu0_init
104 |
105 | // Not CPU0 -- clear the SCRPAD register and wait for it to change.
106 | ldr r2, scrpad_addr
107 | str r0, [r2]
108 | dsb st
109 | sev
110 | 1: wfe
111 | ldr r3, [r2]
112 | cmp r3, #0
113 | beq 1b
114 |
115 | // SCRPAD is no longer NULL, so jump there.
116 | bx r3
117 | .size reset, . - reset
118 |
119 | .type scrpad_addr, %object
120 | scrpad_addr:
121 | .word 0xF080013C
122 | .size scrpad_addr, . - scrpad_addr
123 |
124 | .type cpu0_init, %function
125 | cpu0_init:
126 | ldr r1, sram_base_addr
127 | add sp, r1, #SRAM_SIZE
128 |
129 | // Copy vectors from ROM to SRAM.
130 | ldr r3, rom_base_addr
131 | mov r2, #0x100
132 | 1: ldmia r3!, {r4 - r11}
133 | stmia r1!, {r4 - r11}
134 | subs r2, #32
135 | bgt 1b
136 |
137 | // Copy data from ROM to SRAM.
138 | ldr r3, etext_addr
139 | ldr r2, edata_addr
140 | 1: ldmia r3!, {r4 - r11}
141 | stmia r1!, {r4 - r11}
142 | cmp r1, r2
143 | blt 1b
144 |
145 | // Zero the BSS section.
146 | ldr r2, end_addr
147 | 1: stmia r1!, {r0}
148 | cmp r1, r2
149 | blt 1b
150 |
151 | // Load the boot image into SRAM. Returns the entry address.
152 | bl load_boot_image
153 |
154 | // Jump to the boot image. Panic if it returns back to us.
155 | blx r0
156 | b panic
157 |
158 | .size cpu0_init, . - cpu0_init
159 |
160 | .type sram_base_addr, %object
161 | sram_base_addr:
162 | .word 0xFFFD0000
163 | .size sram_base_addr, . - sram_base_addr
164 |
165 | .type rom_base_addr, %object
166 | rom_base_addr:
167 | .word 0xFFFF0000
168 | .size rom_base_addr, . - rom_base_addr
169 |
170 | .type etext_addr, %object
171 | etext_addr:
172 | .word _etext
173 | .size etext_addr, . - etext_addr
174 |
175 | .type edata_addr, %object
176 | edata_addr:
177 | .word _edata
178 | .size edata_addr, . - edata_addr
179 |
180 | .type end_addr, %object
181 | end_addr:
182 | .word _end
183 | .size end_addr, . - end_addr
184 |
--------------------------------------------------------------------------------
/npcm8xx/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | CROSS_COMPILE ?= aarch64-linux-gnu-
16 |
17 | CC = $(CROSS_COMPILE)gcc
18 | OBJCOPY = $(CROSS_COMPILE)objcopy
19 |
20 | CFLAGS = -Os -g -mcpu=cortex-a35
21 | ASFLAGS = $(CFLAGS) -Wa,-mcpu=cortex-a35
22 | LDSCRIPT = bootrom.ld
23 | LDFLAGS = -Wl,--build-id=none -static -nostdlib -T $(LDSCRIPT)
24 |
25 | OBJS := start.o image.o
26 |
27 | .PHONY: all clean
28 | all: npcm8xx_bootrom.bin
29 |
30 | clean:
31 | rm -f *.o *.bin *.elf
32 |
33 | npcm8xx_bootrom.bin: npcm8xx_bootrom.elf
34 | $(OBJCOPY) -O binary $< $@
35 |
36 | npcm8xx_bootrom.elf: $(OBJS) $(LDSCRIPT)
37 | $(CC) -o $@ $(LDFLAGS) $(OBJS)
38 |
--------------------------------------------------------------------------------
/npcm8xx/README.md:
--------------------------------------------------------------------------------
1 | # Virtual Boot ROM for NPCM7xx SoCs
2 |
3 | This is not an officially supported Google product.
4 |
5 | This is a super simple Boot ROM that is intended to be used as a `-bios` image
6 | for [QEMU](http://www.qemu.org/) when emulating an NPCM8xx-based machine.
7 |
8 | ## Building
9 |
10 | If you have a 64-bit ARM compiler installed as `aarch64-linux-gnu-gcc`, simply run
11 | `make`.
12 |
13 | If your ARM compiler has a different name, you'll need to override the
14 | `CROSS_COMPILE` prefix, e.g. like this:
15 |
16 | ```
17 | make CROSS_COMPILE=aarch64-linux-gnueabi-
18 | ```
19 |
20 | If either case is successful, a `npcm8xx_bootrom.bin` file will be produced.
21 |
22 | ## Using
23 |
24 | The Boot ROM image may be passed to a QEMU system emulator using the `-bios` option. For example like this:
25 |
26 | ```
27 | qemu-system-aarch64 -machine npcm845-evb -nographic \
28 | -bios "${IMAGES}/npcm8xx_bootrom.bin"
29 | -drive file="${IMAGES}/image-bmc,if=mtd,bus=0,unit=0,format=raw,snapshot=on"
30 | ```
31 |
32 | ## Limitations
33 |
34 | * Secure boot is not supported.
35 | * Only booting from offset 0 of the flash at SPI0 CS0 is implemented.
36 | * Fallback images (if the first image doesn't boot) are not implemented.
37 | * Exception vectors are copied to SRAM, but not remapped.
38 | * Most OTP bits and straps are not honored.
39 | * The reset type bits are not updated.
40 | * OTP protection is not implemented.
41 | * No clock initialization is performed.
42 | * UART programming protocol is not implemented.
43 | * Host notification through the PCI mailbox is not implemented.
44 | * Most fields in the ROM status structure are not set.
45 |
--------------------------------------------------------------------------------
/npcm8xx/bootrom.ld:
--------------------------------------------------------------------------------
1 | /*
2 | * Linker script for the Boot ROM.
3 | *
4 | * Copyright 2020 Google LLC
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | MEMORY
20 | {
21 | rom (rx) : ORIGIN = 0x00000000, LENGTH = 32K
22 | ram (a!rx) : ORIGIN = 0xFFFB0000, LENGTH = 256K
23 | }
24 |
25 | SECTIONS
26 | {
27 | /* Vectors are loaded into ROM, and copied into SRAM. */
28 | .text.vectors : {
29 | *(.text.vectors)
30 | . = 0x100;
31 | } >rom AT>ram
32 | /* The rest of the code follows the vectors, but is not copied. */
33 | .text : {
34 | *(.text .text.*)
35 | *(.rodata .rodata.*)
36 | . = ALIGN(32);
37 | _etext = .;
38 | } >rom
39 | /*
40 | * Data follows the code in ROM, and is copied after the vectors in RAM.
41 | * 32-byte aligned so we can use simple and fast copy loops.
42 | */
43 | .data : {
44 | _data = .;
45 | *(.data.rom_status)
46 | *(.data .data.*)
47 | . = ALIGN(32);
48 | _edata = .;
49 | } >rom AT>ram
50 | /* BSS lives in RAM, after the data section. */
51 | .bss : {
52 | *(.bss .bss.*)
53 | . = ALIGN(32);
54 | _end = .;
55 | } >ram
56 | }
57 |
--------------------------------------------------------------------------------
/npcm8xx/image.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Boot image parsing and loading.
3 | *
4 | * Copyright 2020 Google LLC
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | #include
20 |
21 | #define SPI0CS0 0x80000000
22 | #define CLK 0xf0801000
23 | #define FIU0 0xfb000000
24 |
25 | #define CLK_CLKSEL 0x04
26 | #define CLK_CLKSEL_DEFAULT 0x1f18fc9
27 |
28 | #define FIU_DRD_CFG 0x00
29 |
30 | #define UBOOT_TAG 0x4c42544f4f42550aULL /* .UBOOTBL */
31 | #define UBOOT_MAGIC 0x1400000a
32 | #define UBOOT_SIZE 0xb0000
33 |
34 | /*
35 | * This structure must reside at offset 0x100 in SRAM.
36 | *
37 | * See the Check_ROMCode_Status function in the Nuvoton bootblock:
38 | * https://github.com/Nuvoton-Israel/bootblock/blob/master/Src/bootblock_main.c#L795
39 | */
40 | struct rom_status {
41 | uint8_t reserved[12];
42 | uint8_t start_tag[8];
43 | uint32_t status;
44 | } rom_status __attribute__((section(".data.rom_status"))) = {
45 | .status = 0x21, /* SPI0 CS0 offset 0 */
46 | };
47 |
48 | extern void panic(const char *);
49 |
50 | static void reg_write(uintptr_t base, uintptr_t offset, uint32_t value)
51 | {
52 | asm volatile("str %w0, [%1, %2]"
53 | :
54 | : "r"(value), "r"(base), "i"(offset)
55 | : "memory");
56 | }
57 |
58 | static uint32_t image_read_u8(uintptr_t base, uintptr_t offset)
59 | {
60 | return *(uint8_t *)(base + offset);
61 | }
62 |
63 | static uint32_t image_read_u32(uintptr_t base, uintptr_t offset)
64 | {
65 | return *(uint32_t *)(base + offset);
66 | }
67 |
68 | void copy_boot_image(uintptr_t dest_addr, uintptr_t src_addr, int32_t len)
69 | {
70 | uint32_t *dst = (uint32_t *)dest_addr;
71 | uint32_t *src = (uint32_t *)src_addr;
72 |
73 | while (len > 0) {
74 | *dst++ = *src++;
75 | len -= sizeof(*dst);
76 | }
77 | }
78 |
79 | static uint32_t search_and_load_uboot(uintptr_t end)
80 | {
81 | uintptr_t addr;
82 | uintptr_t src_addr, dest_addr;
83 | uint32_t size;
84 |
85 | for (addr = 0; addr < end; addr += 0x100) {
86 | src_addr = SPI0CS0 + addr;
87 | if ((*(uint64_t *)src_addr == UBOOT_TAG) &&
88 | (*(uint32_t *)(src_addr + 0x200) == UBOOT_MAGIC)) {
89 | dest_addr = image_read_u32(src_addr, 0x1f8);
90 | size = image_read_u32(src_addr, 0x1fc);
91 | copy_boot_image(dest_addr, src_addr, size);
92 | return dest_addr;
93 | }
94 | }
95 | return 0;
96 | }
97 |
98 | uintptr_t load_boot_image(void)
99 | {
100 | /* Set CLKSEL to similar values as NPCM7XX */
101 | reg_write(CLK, CLK_CLKSEL, CLK_CLKSEL_DEFAULT);
102 | return search_and_load_uboot(0x200000) + 0x200;
103 | }
104 |
--------------------------------------------------------------------------------
/npcm8xx/start.S:
--------------------------------------------------------------------------------
1 | /*
2 | * Top-level entry points to the Boot ROM. This includes:
3 | * - Reset, exception and interrupt vectors.
4 | * - C run-time initialization.
5 | * - Secondary CPU boot code.
6 | *
7 | * Copyright 2022 Google LLC
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 |
22 | #define KiB (1024)
23 |
24 | #define SRAM_SIZE (256 * KiB)
25 |
26 | .section .text.vectors, "ax"
27 |
28 | .global _start
29 | .type _start, %function
30 | _start:
31 | b reset
32 | . = 0x04
33 | b undefined_instruction
34 | . = 0x08
35 | b software_interrupt
36 | . = 0x0c
37 | b prefetch_abort
38 | . = 0x10
39 | b data_abort
40 | . = 0x18
41 | b interrupt
42 | . = 0x1c
43 | b fast_interrupt
44 |
45 | undefined_instruction:
46 | mov x0, #1
47 | b handle_exception
48 |
49 | software_interrupt:
50 | mov x0, #2
51 | b handle_exception
52 |
53 | prefetch_abort:
54 | mov x0, #3
55 | b handle_exception
56 |
57 | data_abort:
58 | mov x0, #4
59 | b handle_exception
60 |
61 | interrupt:
62 | mov x0, #6
63 | b handle_exception
64 |
65 | fast_interrupt:
66 | mov x0, #7
67 | b handle_exception
68 |
69 | vectors_end:
70 |
71 | .text
72 | .align 2
73 | handle_exception:
74 |
75 | .global panic
76 | .type panic, %function
77 | panic:
78 | 1: wfi
79 | b 1b
80 | .size panic, . - panic
81 |
82 | .type reset, %function
83 | reset:
84 | mov x0, #0
85 | // Read the CPU ID from MPIDR_EL1.
86 | mrs x1, MPIDR_EL1
87 | and x1, x1, #0x03
88 | cbz x1, cpu0_init
89 |
90 | // Not CPU0 -- clear the SCRPAD register and wait for it to change.
91 | ldr x2, scrpad_addr
92 | str x0, [x2]
93 | dsb st
94 | sev
95 | 1: wfe
96 | ldr x3, [x2]
97 | cmp x3, #0
98 | beq 1b
99 |
100 | // SCRPAD is no longer NULL, so jump there.
101 | ret x3
102 | .size reset, . - reset
103 |
104 | .type scrpad_addr, %object
105 | scrpad_addr:
106 | .dword 0xF0800E00
107 | .size scrpad_addr, . - scrpad_addr
108 |
109 | .type cpu0_init, %function
110 | cpu0_init:
111 | ldr x1, sram_base_addr
112 | add sp, x1, #SRAM_SIZE
113 |
114 | // Load the boot image into SRAM. Returns the entry address.
115 | bl load_boot_image
116 |
117 | // Jump to the boot image. Panic if it returns back to us.
118 | ret x0
119 | b panic
120 |
121 | .size cpu0_init, . - cpu0_init
122 |
123 | .type sram_base_addr, %object
124 | sram_base_addr:
125 | .dword 0xFFFB0000
126 | .size sram_base_addr, . - sram_base_addr
127 |
128 | .type sdram_base_addr, %object
129 | sdram_base_addr:
130 | .dword 0x00000000
131 | .size sdram_base_addr, . - sdram_base_addr
132 |
133 | .type etext_addr, %object
134 | etext_addr:
135 | .dword _etext
136 | .size etext_addr, . - etext_addr
137 |
138 | .type edata_addr, %object
139 | edata_addr:
140 | .dword _edata
141 | .size edata_addr, . - edata_addr
142 |
143 | .type end_addr, %object
144 | end_addr:
145 | .dword _end
146 | .size end_addr, . - end_addr
147 |
--------------------------------------------------------------------------------