blob: 176755cb2add7d85c0a58ac7d18b40b45417a223 [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 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Andrew Scull18834872018-10-12 11:48:09 +01007 */
8
Andrew Scull18c78fc2018-08-20 12:57:41 +01009#include "hf/fdt.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010010
David Brazdilb856be62020-03-25 10:14:55 +000011#include <libfdt.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010012
David Brazdilb856be62020-03-25 10:14:55 +000013#include "hf/static_assert.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010014
David Brazdilb856be62020-03-25 10:14:55 +000015/** Returns pointer to the FDT buffer. */
16const void *fdt_base(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010017{
David Brazdilb856be62020-03-25 10:14:55 +000018 return memiter_base(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010019}
20
David Brazdilb856be62020-03-25 10:14:55 +000021/** Returns size of the FDT buffer. */
22size_t fdt_size(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010023{
David Brazdilb856be62020-03-25 10:14:55 +000024 return memiter_size(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025}
26
David Brazdil7a462ec2019-08-15 12:27:47 +010027/**
David Brazdilb856be62020-03-25 10:14:55 +000028 * Extracts total size of the FDT structure from its FDT header.
29 * Returns true on success, false if header validation failed.
David Brazdil7a462ec2019-08-15 12:27:47 +010030 */
David Brazdilb856be62020-03-25 10:14:55 +000031bool fdt_size_from_header(const void *ptr, size_t *val)
David Brazdil7a462ec2019-08-15 12:27:47 +010032{
David Brazdilb856be62020-03-25 10:14:55 +000033 if (fdt_check_header(ptr) != 0) {
34 return false;
35 }
David Brazdil7a462ec2019-08-15 12:27:47 +010036
David Brazdilb856be62020-03-25 10:14:55 +000037 *val = fdt_totalsize(ptr);
38 return true;
39}
40
41/**
42 * Initializes `struct fdt` to point to a given buffer.
43 * Returns true on success, false if FDT validation failed.
44 */
45bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len)
46{
47 if (fdt_check_full(ptr, len) != 0) {
48 return false;
49 }
50
51 memiter_init(&fdt->buf, ptr, len);
52 return true;
53}
54
55/**
56 * Initializes `struct fdt` to point to a given buffer.
57 * Returns true on success, false if FDT validation failed.
58 */
59bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it)
60{
61 return fdt_init_from_ptr(fdt, memiter_base(it), memiter_size(it));
62}
63
64/**
65 * Invalidates the internal pointer to FDT buffer.
66 * This is meant to prevent use-after-free bugs.
67 */
68void fdt_fini(struct fdt *fdt)
69{
70 memiter_init(&fdt->buf, NULL, 0);
71}
72
73/**
74 * Finds a node of a given path in the device tree.
75 * Unit addresses of components may be omitted but result is undefined if
76 * the path is not unique.
77 * Returns true on success, false if not found or an error occurred.
78 */
79bool fdt_find_node(const struct fdt *fdt, const char *path,
80 struct fdt_node *node)
81{
82 int offset = fdt_path_offset(fdt_base(fdt), path);
83
84 if (offset < 0) {
85 return false;
86 }
87
88 *node = (struct fdt_node){.fdt = *fdt, .offset = offset};
89 return true;
90}
91
92/**
93 * Retrieves address size for a bus represented in the device tree.
94 * Result is value of '#address-cells' at `node` multiplied by cell size.
95 * If '#address-cells' is not found, the default value is 2 cells.
96 * Returns true on success, false if an error occurred.
97 */
98bool fdt_address_size(const struct fdt_node *node, size_t *size)
99{
100 int s = fdt_address_cells(fdt_base(&node->fdt), node->offset);
101
102 if (s < 0) {
103 return false;
104 }
105
106 *size = (size_t)s * sizeof(uint32_t);
107 return true;
108}
109
110/**
111 * Retrieves address range size for a bus represented in the device tree.
112 * Result is value of '#size-cells' at `node` multiplied by cell size.
113 * If '#size-cells' is not found, the default value is 1 cell.
114 * Returns true on success, false if an error occurred.
115 */
116bool fdt_size_size(const struct fdt_node *node, size_t *size)
117{
118 int s = fdt_size_cells(fdt_base(&node->fdt), node->offset);
119
120 if (s < 0) {
121 return false;
122 }
123
124 *size = (size_t)s * sizeof(uint32_t);
125 return true;
126}
127
128/**
129 * Retrieves the buffer with value of property `name` at `node`.
130 * Returns true on success, false if not found or an error occurred.
131 */
132bool fdt_read_property(const struct fdt_node *node, const char *name,
133 struct memiter *data)
134{
135 const void *ptr;
136 int lenp;
137
138 ptr = fdt_getprop(fdt_base(&node->fdt), node->offset, name, &lenp);
139 if (ptr == NULL) {
140 return false;
141 }
142
143 CHECK(lenp >= 0);
144 memiter_init(data, ptr, (size_t)lenp);
145 return true;
146}
147
148/**
149 * Reads the value of property `name` at `node` as a uint.
150 * The size of the uint is inferred from the size of the property's value.
151 * Returns true on success, false if property not found or an error occurred.
152 */
153bool fdt_read_number(const struct fdt_node *node, const char *name,
154 uint64_t *val)
155{
156 struct memiter data;
157
158 return fdt_read_property(node, name, &data) &&
159 fdt_parse_number(&data, memiter_size(&data), val) &&
160 (memiter_size(&data) == 0);
161}
162
163/**
164 * Parses a uint of given `size` from the beginning of `data`.
165 * On success returns true and advances `data` by `size` bytes.
166 * Returns false if `data` is too short or uints of `size` are not supported.
167 */
168bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val)
169{
170 struct memiter data_int;
171 struct memiter data_rem;
172
173 data_rem = *data;
174 if (!memiter_consume(&data_rem, size, &data_int)) {
175 return false;
176 }
David Brazdil7a462ec2019-08-15 12:27:47 +0100177
178 switch (size) {
David Brazdilb856be62020-03-25 10:14:55 +0000179 case sizeof(uint32_t): {
180 static_assert(sizeof(uint32_t) == sizeof(fdt32_t),
181 "Size mismatch");
182 *val = fdt32_ld((const fdt32_t *)memiter_base(&data_int));
183 break;
184 }
185 case sizeof(uint64_t): {
186 static_assert(sizeof(uint64_t) == sizeof(fdt64_t),
187 "Size mismatch");
188 *val = fdt64_ld((const fdt64_t *)memiter_base(&data_int));
189 break;
190 }
191 default: {
David Brazdil7a462ec2019-08-15 12:27:47 +0100192 return false;
193 }
Andrew Scull7364a8e2018-07-19 15:39:29 +0100194 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100195
David Brazdilb856be62020-03-25 10:14:55 +0000196 *data = data_rem;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100197 return true;
198}
199
David Brazdilb856be62020-03-25 10:14:55 +0000200/**
201 * Finds first direct subnode of `node`.
202 * If found, makes `node` point to the subnode and returns true.
203 * Returns false if no subnode is found.
204 */
205bool fdt_first_child(struct fdt_node *node)
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100206{
David Brazdilb856be62020-03-25 10:14:55 +0000207 int child_off = fdt_first_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100208
David Brazdilb856be62020-03-25 10:14:55 +0000209 if (child_off < 0) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100210 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100211 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100212
David Brazdilb856be62020-03-25 10:14:55 +0000213 node->offset = child_off;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100214 return true;
215}
216
David Brazdilb856be62020-03-25 10:14:55 +0000217/**
218 * Finds next sibling node of `node`. Call repeatedly to discover all siblings.
219 * If found, makes `node` point to the next sibling node and returns true.
220 * Returns false if no next sibling node is found.
221 */
222bool fdt_next_sibling(struct fdt_node *node)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100223{
David Brazdilb856be62020-03-25 10:14:55 +0000224 int sib_off = fdt_next_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100225
David Brazdilb856be62020-03-25 10:14:55 +0000226 if (sib_off < 0) {
227 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100228 }
229
David Brazdilb856be62020-03-25 10:14:55 +0000230 node->offset = sib_off;
231 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100232}
233
David Brazdilb856be62020-03-25 10:14:55 +0000234/**
235 * Finds a node named `name` among subnodes of `node`.
236 * Returns true if found, false if not found or an error occurred.
237 */
238bool fdt_find_child(struct fdt_node *node, const struct string *name)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100239{
David Brazdilb856be62020-03-25 10:14:55 +0000240 struct fdt_node child = *node;
241 const void *base = fdt_base(&node->fdt);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100242
David Brazdilb856be62020-03-25 10:14:55 +0000243 if (!fdt_first_child(&child)) {
244 return false;
Andrew Scull83644292018-10-05 22:38:46 +0100245 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100246
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247 do {
David Brazdilb856be62020-03-25 10:14:55 +0000248 const char *child_name;
249 int lenp;
250 struct memiter it;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251
David Brazdilb856be62020-03-25 10:14:55 +0000252 child_name = fdt_get_name(base, child.offset, &lenp);
253 if (child_name == NULL) {
254 /* Error */
255 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256 }
257
David Brazdilb856be62020-03-25 10:14:55 +0000258 CHECK(lenp >= 0);
259 memiter_init(&it, child_name, (size_t)lenp);
260 if (string_eq(name, &it)) {
261 node->offset = child.offset;
262 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100263 }
David Brazdilb856be62020-03-25 10:14:55 +0000264 } while (fdt_next_sibling(&child));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100265
David Brazdilb856be62020-03-25 10:14:55 +0000266 /* Not found */
267 return false;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100268}
David Brazdilf4925382020-03-25 13:33:51 +0000269
270/**
271 * Returns true if `node` has property "compatible" containing a `compat` entry.
272 * Returns false if node not compatible or an error occurred.
273 */
274bool fdt_is_compatible(struct fdt_node *node, const char *compat)
275{
276 return fdt_node_check_compatible(fdt_base(&node->fdt), node->offset,
277 compat) == 0;
278}