blob: 1983fc6fccc28c06b9687c52e59e0d9063a5b763 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
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 * https://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
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/fdt.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
David Brazdilb856be62020-03-25 10:14:55 +000019#include <libfdt.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010020
David Brazdilb856be62020-03-25 10:14:55 +000021#include "hf/static_assert.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010022
David Brazdilb856be62020-03-25 10:14:55 +000023/** Returns pointer to the FDT buffer. */
24const void *fdt_base(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025{
David Brazdilb856be62020-03-25 10:14:55 +000026 return memiter_base(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010027}
28
David Brazdilb856be62020-03-25 10:14:55 +000029/** Returns size of the FDT buffer. */
30size_t fdt_size(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010031{
David Brazdilb856be62020-03-25 10:14:55 +000032 return memiter_size(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010033}
34
David Brazdil7a462ec2019-08-15 12:27:47 +010035/**
David Brazdilb856be62020-03-25 10:14:55 +000036 * Extracts total size of the FDT structure from its FDT header.
37 * Returns true on success, false if header validation failed.
David Brazdil7a462ec2019-08-15 12:27:47 +010038 */
David Brazdilb856be62020-03-25 10:14:55 +000039bool fdt_size_from_header(const void *ptr, size_t *val)
David Brazdil7a462ec2019-08-15 12:27:47 +010040{
David Brazdilb856be62020-03-25 10:14:55 +000041 if (fdt_check_header(ptr) != 0) {
42 return false;
43 }
David Brazdil7a462ec2019-08-15 12:27:47 +010044
David Brazdilb856be62020-03-25 10:14:55 +000045 *val = fdt_totalsize(ptr);
46 return true;
47}
48
49/**
50 * Initializes `struct fdt` to point to a given buffer.
51 * Returns true on success, false if FDT validation failed.
52 */
53bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len)
54{
55 if (fdt_check_full(ptr, len) != 0) {
56 return false;
57 }
58
59 memiter_init(&fdt->buf, ptr, len);
60 return true;
61}
62
63/**
64 * Initializes `struct fdt` to point to a given buffer.
65 * Returns true on success, false if FDT validation failed.
66 */
67bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it)
68{
69 return fdt_init_from_ptr(fdt, memiter_base(it), memiter_size(it));
70}
71
72/**
73 * Invalidates the internal pointer to FDT buffer.
74 * This is meant to prevent use-after-free bugs.
75 */
76void fdt_fini(struct fdt *fdt)
77{
78 memiter_init(&fdt->buf, NULL, 0);
79}
80
81/**
82 * Finds a node of a given path in the device tree.
83 * Unit addresses of components may be omitted but result is undefined if
84 * the path is not unique.
85 * Returns true on success, false if not found or an error occurred.
86 */
87bool fdt_find_node(const struct fdt *fdt, const char *path,
88 struct fdt_node *node)
89{
90 int offset = fdt_path_offset(fdt_base(fdt), path);
91
92 if (offset < 0) {
93 return false;
94 }
95
96 *node = (struct fdt_node){.fdt = *fdt, .offset = offset};
97 return true;
98}
99
100/**
101 * Retrieves address size for a bus represented in the device tree.
102 * Result is value of '#address-cells' at `node` multiplied by cell size.
103 * If '#address-cells' is not found, the default value is 2 cells.
104 * Returns true on success, false if an error occurred.
105 */
106bool fdt_address_size(const struct fdt_node *node, size_t *size)
107{
108 int s = fdt_address_cells(fdt_base(&node->fdt), node->offset);
109
110 if (s < 0) {
111 return false;
112 }
113
114 *size = (size_t)s * sizeof(uint32_t);
115 return true;
116}
117
118/**
119 * Retrieves address range size for a bus represented in the device tree.
120 * Result is value of '#size-cells' at `node` multiplied by cell size.
121 * If '#size-cells' is not found, the default value is 1 cell.
122 * Returns true on success, false if an error occurred.
123 */
124bool fdt_size_size(const struct fdt_node *node, size_t *size)
125{
126 int s = fdt_size_cells(fdt_base(&node->fdt), node->offset);
127
128 if (s < 0) {
129 return false;
130 }
131
132 *size = (size_t)s * sizeof(uint32_t);
133 return true;
134}
135
136/**
137 * Retrieves the buffer with value of property `name` at `node`.
138 * Returns true on success, false if not found or an error occurred.
139 */
140bool fdt_read_property(const struct fdt_node *node, const char *name,
141 struct memiter *data)
142{
143 const void *ptr;
144 int lenp;
145
146 ptr = fdt_getprop(fdt_base(&node->fdt), node->offset, name, &lenp);
147 if (ptr == NULL) {
148 return false;
149 }
150
151 CHECK(lenp >= 0);
152 memiter_init(data, ptr, (size_t)lenp);
153 return true;
154}
155
156/**
157 * Reads the value of property `name` at `node` as a uint.
158 * The size of the uint is inferred from the size of the property's value.
159 * Returns true on success, false if property not found or an error occurred.
160 */
161bool fdt_read_number(const struct fdt_node *node, const char *name,
162 uint64_t *val)
163{
164 struct memiter data;
165
166 return fdt_read_property(node, name, &data) &&
167 fdt_parse_number(&data, memiter_size(&data), val) &&
168 (memiter_size(&data) == 0);
169}
170
171/**
172 * Parses a uint of given `size` from the beginning of `data`.
173 * On success returns true and advances `data` by `size` bytes.
174 * Returns false if `data` is too short or uints of `size` are not supported.
175 */
176bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val)
177{
178 struct memiter data_int;
179 struct memiter data_rem;
180
181 data_rem = *data;
182 if (!memiter_consume(&data_rem, size, &data_int)) {
183 return false;
184 }
David Brazdil7a462ec2019-08-15 12:27:47 +0100185
186 switch (size) {
David Brazdilb856be62020-03-25 10:14:55 +0000187 case sizeof(uint32_t): {
188 static_assert(sizeof(uint32_t) == sizeof(fdt32_t),
189 "Size mismatch");
190 *val = fdt32_ld((const fdt32_t *)memiter_base(&data_int));
191 break;
192 }
193 case sizeof(uint64_t): {
194 static_assert(sizeof(uint64_t) == sizeof(fdt64_t),
195 "Size mismatch");
196 *val = fdt64_ld((const fdt64_t *)memiter_base(&data_int));
197 break;
198 }
199 default: {
David Brazdil7a462ec2019-08-15 12:27:47 +0100200 return false;
201 }
Andrew Scull7364a8e2018-07-19 15:39:29 +0100202 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100203
David Brazdilb856be62020-03-25 10:14:55 +0000204 *data = data_rem;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100205 return true;
206}
207
David Brazdilb856be62020-03-25 10:14:55 +0000208/**
209 * Finds first direct subnode of `node`.
210 * If found, makes `node` point to the subnode and returns true.
211 * Returns false if no subnode is found.
212 */
213bool fdt_first_child(struct fdt_node *node)
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100214{
David Brazdilb856be62020-03-25 10:14:55 +0000215 int child_off = fdt_first_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100216
David Brazdilb856be62020-03-25 10:14:55 +0000217 if (child_off < 0) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100218 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100219 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100220
David Brazdilb856be62020-03-25 10:14:55 +0000221 node->offset = child_off;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100222 return true;
223}
224
David Brazdilb856be62020-03-25 10:14:55 +0000225/**
226 * Finds next sibling node of `node`. Call repeatedly to discover all siblings.
227 * If found, makes `node` point to the next sibling node and returns true.
228 * Returns false if no next sibling node is found.
229 */
230bool fdt_next_sibling(struct fdt_node *node)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100231{
David Brazdilb856be62020-03-25 10:14:55 +0000232 int sib_off = fdt_next_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100233
David Brazdilb856be62020-03-25 10:14:55 +0000234 if (sib_off < 0) {
235 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100236 }
237
David Brazdilb856be62020-03-25 10:14:55 +0000238 node->offset = sib_off;
239 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240}
241
David Brazdilb856be62020-03-25 10:14:55 +0000242/**
243 * Finds a node named `name` among subnodes of `node`.
244 * Returns true if found, false if not found or an error occurred.
245 */
246bool fdt_find_child(struct fdt_node *node, const struct string *name)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247{
David Brazdilb856be62020-03-25 10:14:55 +0000248 struct fdt_node child = *node;
249 const void *base = fdt_base(&node->fdt);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250
David Brazdilb856be62020-03-25 10:14:55 +0000251 if (!fdt_first_child(&child)) {
252 return false;
Andrew Scull83644292018-10-05 22:38:46 +0100253 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100255 do {
David Brazdilb856be62020-03-25 10:14:55 +0000256 const char *child_name;
257 int lenp;
258 struct memiter it;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100259
David Brazdilb856be62020-03-25 10:14:55 +0000260 child_name = fdt_get_name(base, child.offset, &lenp);
261 if (child_name == NULL) {
262 /* Error */
263 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100264 }
265
David Brazdilb856be62020-03-25 10:14:55 +0000266 CHECK(lenp >= 0);
267 memiter_init(&it, child_name, (size_t)lenp);
268 if (string_eq(name, &it)) {
269 node->offset = child.offset;
270 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100271 }
David Brazdilb856be62020-03-25 10:14:55 +0000272 } while (fdt_next_sibling(&child));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100273
David Brazdilb856be62020-03-25 10:14:55 +0000274 /* Not found */
275 return false;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100276}
David Brazdilf4925382020-03-25 13:33:51 +0000277
278/**
279 * Returns true if `node` has property "compatible" containing a `compat` entry.
280 * Returns false if node not compatible or an error occurred.
281 */
282bool fdt_is_compatible(struct fdt_node *node, const char *compat)
283{
284 return fdt_node_check_compatible(fdt_base(&node->fdt), node->offset,
285 compat) == 0;
286}