├── .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 | --------------------------------------------------------------------------------