blob: 30941d07a122cd31244d0206d6542477fd494452 [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/** Returns pointer to the FDT buffer. */
14const void *fdt_base(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010015{
David Brazdilb856be62020-03-25 10:14:55 +000016 return memiter_base(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010017}
18
David Brazdilb856be62020-03-25 10:14:55 +000019/** Returns size of the FDT buffer. */
20size_t fdt_size(const struct fdt *fdt)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021{
David Brazdilb856be62020-03-25 10:14:55 +000022 return memiter_size(&fdt->buf);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010023}
24
David Brazdil7a462ec2019-08-15 12:27:47 +010025/**
David Brazdilb856be62020-03-25 10:14:55 +000026 * Extracts total size of the FDT structure from its FDT header.
27 * Returns true on success, false if header validation failed.
David Brazdil7a462ec2019-08-15 12:27:47 +010028 */
David Brazdilb856be62020-03-25 10:14:55 +000029bool fdt_size_from_header(const void *ptr, size_t *val)
David Brazdil7a462ec2019-08-15 12:27:47 +010030{
David Brazdilb856be62020-03-25 10:14:55 +000031 if (fdt_check_header(ptr) != 0) {
32 return false;
33 }
David Brazdil7a462ec2019-08-15 12:27:47 +010034
David Brazdilb856be62020-03-25 10:14:55 +000035 *val = fdt_totalsize(ptr);
36 return true;
37}
38
39/**
40 * Initializes `struct fdt` to point to a given buffer.
41 * Returns true on success, false if FDT validation failed.
42 */
43bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len)
44{
45 if (fdt_check_full(ptr, len) != 0) {
46 return false;
47 }
48
49 memiter_init(&fdt->buf, ptr, len);
50 return true;
51}
52
53/**
54 * Initializes `struct fdt` to point to a given buffer.
55 * Returns true on success, false if FDT validation failed.
56 */
57bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it)
58{
59 return fdt_init_from_ptr(fdt, memiter_base(it), memiter_size(it));
60}
61
62/**
63 * Invalidates the internal pointer to FDT buffer.
64 * This is meant to prevent use-after-free bugs.
65 */
66void fdt_fini(struct fdt *fdt)
67{
68 memiter_init(&fdt->buf, NULL, 0);
69}
70
71/**
72 * Finds a node of a given path in the device tree.
73 * Unit addresses of components may be omitted but result is undefined if
74 * the path is not unique.
75 * Returns true on success, false if not found or an error occurred.
76 */
77bool fdt_find_node(const struct fdt *fdt, const char *path,
78 struct fdt_node *node)
79{
80 int offset = fdt_path_offset(fdt_base(fdt), path);
81
82 if (offset < 0) {
83 return false;
84 }
85
86 *node = (struct fdt_node){.fdt = *fdt, .offset = offset};
87 return true;
88}
89
90/**
91 * Retrieves address size for a bus represented in the device tree.
92 * Result is value of '#address-cells' at `node` multiplied by cell size.
93 * If '#address-cells' is not found, the default value is 2 cells.
94 * Returns true on success, false if an error occurred.
95 */
96bool fdt_address_size(const struct fdt_node *node, size_t *size)
97{
98 int s = fdt_address_cells(fdt_base(&node->fdt), node->offset);
99
100 if (s < 0) {
101 return false;
102 }
103
104 *size = (size_t)s * sizeof(uint32_t);
105 return true;
106}
107
108/**
109 * Retrieves address range size for a bus represented in the device tree.
110 * Result is value of '#size-cells' at `node` multiplied by cell size.
111 * If '#size-cells' is not found, the default value is 1 cell.
112 * Returns true on success, false if an error occurred.
113 */
114bool fdt_size_size(const struct fdt_node *node, size_t *size)
115{
116 int s = fdt_size_cells(fdt_base(&node->fdt), node->offset);
117
118 if (s < 0) {
119 return false;
120 }
121
122 *size = (size_t)s * sizeof(uint32_t);
123 return true;
124}
125
126/**
127 * Retrieves the buffer with value of property `name` at `node`.
128 * Returns true on success, false if not found or an error occurred.
129 */
130bool fdt_read_property(const struct fdt_node *node, const char *name,
131 struct memiter *data)
132{
133 const void *ptr;
134 int lenp;
135
136 ptr = fdt_getprop(fdt_base(&node->fdt), node->offset, name, &lenp);
137 if (ptr == NULL) {
138 return false;
139 }
140
141 CHECK(lenp >= 0);
142 memiter_init(data, ptr, (size_t)lenp);
143 return true;
144}
145
146/**
147 * Reads the value of property `name` at `node` as a uint.
148 * The size of the uint is inferred from the size of the property's value.
149 * Returns true on success, false if property not found or an error occurred.
150 */
151bool fdt_read_number(const struct fdt_node *node, const char *name,
152 uint64_t *val)
153{
154 struct memiter data;
155
156 return fdt_read_property(node, name, &data) &&
157 fdt_parse_number(&data, memiter_size(&data), val) &&
158 (memiter_size(&data) == 0);
159}
160
161/**
162 * Parses a uint of given `size` from the beginning of `data`.
163 * On success returns true and advances `data` by `size` bytes.
164 * Returns false if `data` is too short or uints of `size` are not supported.
165 */
166bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val)
167{
168 struct memiter data_int;
169 struct memiter data_rem;
170
171 data_rem = *data;
172 if (!memiter_consume(&data_rem, size, &data_int)) {
173 return false;
174 }
David Brazdil7a462ec2019-08-15 12:27:47 +0100175
176 switch (size) {
David Brazdilb856be62020-03-25 10:14:55 +0000177 case sizeof(uint32_t): {
178 static_assert(sizeof(uint32_t) == sizeof(fdt32_t),
179 "Size mismatch");
180 *val = fdt32_ld((const fdt32_t *)memiter_base(&data_int));
181 break;
182 }
183 case sizeof(uint64_t): {
184 static_assert(sizeof(uint64_t) == sizeof(fdt64_t),
185 "Size mismatch");
186 *val = fdt64_ld((const fdt64_t *)memiter_base(&data_int));
187 break;
188 }
189 default: {
David Brazdil7a462ec2019-08-15 12:27:47 +0100190 return false;
191 }
Andrew Scull7364a8e2018-07-19 15:39:29 +0100192 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100193
David Brazdilb856be62020-03-25 10:14:55 +0000194 *data = data_rem;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100195 return true;
196}
197
David Brazdilb856be62020-03-25 10:14:55 +0000198/**
199 * Finds first direct subnode of `node`.
200 * If found, makes `node` point to the subnode and returns true.
201 * Returns false if no subnode is found.
202 */
203bool fdt_first_child(struct fdt_node *node)
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100204{
David Brazdilb856be62020-03-25 10:14:55 +0000205 int child_off = fdt_first_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100206
David Brazdilb856be62020-03-25 10:14:55 +0000207 if (child_off < 0) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100208 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100209 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100210
David Brazdilb856be62020-03-25 10:14:55 +0000211 node->offset = child_off;
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100212 return true;
213}
214
David Brazdilb856be62020-03-25 10:14:55 +0000215/**
216 * Finds next sibling node of `node`. Call repeatedly to discover all siblings.
217 * If found, makes `node` point to the next sibling node and returns true.
218 * Returns false if no next sibling node is found.
219 */
220bool fdt_next_sibling(struct fdt_node *node)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100221{
David Brazdilb856be62020-03-25 10:14:55 +0000222 int sib_off = fdt_next_subnode(fdt_base(&node->fdt), node->offset);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100223
David Brazdilb856be62020-03-25 10:14:55 +0000224 if (sib_off < 0) {
225 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100226 }
227
David Brazdilb856be62020-03-25 10:14:55 +0000228 node->offset = sib_off;
229 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100230}
231
David Brazdilb856be62020-03-25 10:14:55 +0000232/**
233 * Finds a node named `name` among subnodes of `node`.
234 * Returns true if found, false if not found or an error occurred.
235 */
236bool fdt_find_child(struct fdt_node *node, const struct string *name)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100237{
David Brazdilb856be62020-03-25 10:14:55 +0000238 struct fdt_node child = *node;
239 const void *base = fdt_base(&node->fdt);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240
David Brazdilb856be62020-03-25 10:14:55 +0000241 if (!fdt_first_child(&child)) {
242 return false;
Andrew Scull83644292018-10-05 22:38:46 +0100243 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100245 do {
David Brazdilb856be62020-03-25 10:14:55 +0000246 const char *child_name;
247 int lenp;
248 struct memiter it;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249
David Brazdilb856be62020-03-25 10:14:55 +0000250 child_name = fdt_get_name(base, child.offset, &lenp);
251 if (child_name == NULL) {
252 /* Error */
253 return false;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 }
255
David Brazdilb856be62020-03-25 10:14:55 +0000256 CHECK(lenp >= 0);
257 memiter_init(&it, child_name, (size_t)lenp);
258 if (string_eq(name, &it)) {
259 node->offset = child.offset;
260 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100261 }
David Brazdilb856be62020-03-25 10:14:55 +0000262 } while (fdt_next_sibling(&child));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100263
David Brazdilb856be62020-03-25 10:14:55 +0000264 /* Not found */
265 return false;
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100266}
David Brazdilf4925382020-03-25 13:33:51 +0000267
268/**
269 * Returns true if `node` has property "compatible" containing a `compat` entry.
270 * Returns false if node not compatible or an error occurred.
271 */
272bool fdt_is_compatible(struct fdt_node *node, const char *compat)
273{
274 return fdt_node_check_compatible(fdt_base(&node->fdt), node->offset,
275 compat) == 0;
276}