1 /*
2 * Copyright (C) 2022 The Android Open Source Project
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 #define TLOG_TAG "device_tree_service_lib"
18
19 #include <lk/compiler.h>
20 #include <lk/trace.h>
21 #include <trusty_log.h>
22
23 #include <libfdt.h>
24
25 #include <lib/shared/device_tree/service/device_tree_service.h>
26
27 #define LOCAL_TRACE 0
28
29 using com::android::trusty::device_tree::DeviceTree;
30 using com::android::trusty::device_tree::IDeviceTree;
31
32 namespace com {
33 namespace android {
34 namespace trusty {
35 namespace device_tree {
36
37 // Returns the offset of the first node with one of the given `compatible`
38 // strings or `nullopt` if no matching nodes were found. This is analogous to
39 // fdt_node_offset_by_compatible in libfdt.
NodeOffsetByCompatibleList(const void * dtb,int startoffset,const std::vector<std::string> & compatible_strs)40 static std::optional<int> NodeOffsetByCompatibleList(
41 const void* dtb,
42 int startoffset,
43 const std::vector<std::string>& compatible_strs) {
44 // For each compatible string, find the first matching node's offset and
45 // keep the minimum of all node offsets
46 int min_node_offset = INT_MAX;
47 for (const auto& compat : compatible_strs) {
48 int node_offset = ::fdt_node_offset_by_compatible(dtb, startoffset,
49 compat.c_str());
50 if (node_offset < 0) {
51 // Other return codes indicate a dtb in an invalid state
52 assert(node_offset == -FDT_ERR_NOTFOUND);
53
54 // No nodes matching `compat` were found
55 continue;
56 }
57
58 min_node_offset = std::min(min_node_offset, node_offset);
59 }
60
61 // If min_node_offset is unchanged, no matching nodes were found
62 if (min_node_offset == INT_MAX) {
63 return std::nullopt;
64 }
65
66 return std::optional<int>{min_node_offset};
67 }
68
DeviceTree(const unsigned char * dtb,size_t dtb_size)69 DeviceTree::DeviceTree(const unsigned char* dtb, size_t dtb_size)
70 : BnDeviceTree(), mDtb(dtb) {
71 int rc = ::fdt_check_full(dtb, dtb_size);
72 assert(rc == 0);
73 }
74
get_compatible_nodes_from_list(const std::vector<std::string> & compatible_strs,sp<INodeIterator> * node_iter)75 Status DeviceTree::get_compatible_nodes_from_list(
76 const std::vector<std::string>& compatible_strs,
77 sp<INodeIterator>* node_iter) {
78 if (node_iter == nullptr) {
79 TLOGE("Invalid arguments\n");
80 return Status::fromServiceSpecificError(
81 IDeviceTree::ERROR_INVALID_ARGS);
82 }
83
84 auto min_node_offset = NodeOffsetByCompatibleList(
85 mDtb, -1 /* search from start */, compatible_strs);
86 if (!min_node_offset.has_value()) {
87 TLOGI("No nodes matching the compatible strings found\n");
88 return Status::fromServiceSpecificError(
89 IDeviceTree::ERROR_NODE_NOT_FOUND);
90 }
91
92 sp<NodeIterator> iter = sp<NodeIterator>::make(std::move(compatible_strs),
93 *min_node_offset, mDtb);
94 if (iter == nullptr) {
95 TLOGE("Failed to allocate memory for NodeIterator\n");
96 return Status::fromServiceSpecificError(IDeviceTree::ERROR_NO_MEMORY);
97 }
98 *node_iter = iter;
99
100 return Status::ok();
101 }
102
get_next_node(sp<INode> * node)103 Status NodeIterator::get_next_node(sp<INode>* node) {
104 if (node == nullptr) {
105 TLOGE("Invalid arguments\n");
106 return Status::fromServiceSpecificError(
107 IDeviceTree::ERROR_INVALID_ARGS);
108 }
109
110 // If the current node offset is -1, use the node offset that was
111 // passed in to the constructor
112 if (mCurrentNodeOffset == -1) {
113 mCurrentNodeOffset = mInitialNodeOffset;
114 } else {
115 // Advance current node offset to the next node
116 if (mCompatibleStrs.has_value()) {
117 // Search for next node with matching compatible strings
118 auto min_node_offset = NodeOffsetByCompatibleList(
119 mDtb, mCurrentNodeOffset, *mCompatibleStrs);
120
121 if (!min_node_offset.has_value()) {
122 TLOGI("Reached the end of the node iterator\n");
123 return Status::fromServiceSpecificError(
124 IDeviceTree::ERROR_NODE_NOT_FOUND);
125 }
126
127 mCurrentNodeOffset = *min_node_offset;
128
129 } else {
130 // Search for next subnode
131 int next_subnode = ::fdt_next_subnode(mDtb, mCurrentNodeOffset);
132
133 if (next_subnode < 0) {
134 // Other return codes indicate a dtb in an invalid state
135 assert(next_subnode == -FDT_ERR_NOTFOUND);
136
137 TLOGI("Reached the end of the node iterator\n");
138 return Status::fromServiceSpecificError(
139 IDeviceTree::ERROR_NODE_NOT_FOUND);
140 }
141
142 mCurrentNodeOffset = next_subnode;
143 }
144 }
145
146 // mCurrentNodeOffset was either initialized to a valid node offset
147 // by the constructor or advanced to another valid node offset by a
148 // previous call to `get_next_node`
149 sp<Node> next_node = sp<Node>::make(mCurrentNodeOffset, mDtb);
150 if (next_node == nullptr) {
151 TLOGE("Failed to allocate memory for Node\n");
152 return Status::fromServiceSpecificError(IDeviceTree::ERROR_NO_MEMORY);
153 }
154 *node = next_node;
155
156 return Status::ok();
157 }
158
get_name(std::string * node_name)159 Status Node::get_name(std::string* node_name) {
160 if (node_name == nullptr) {
161 TLOGE("Invalid arguments\n");
162 return Status::fromServiceSpecificError(
163 IDeviceTree::ERROR_INVALID_ARGS);
164 }
165
166 const char* fdt_node_name = ::fdt_get_name(mDtb, mNodeOffset, NULL);
167 if (fdt_node_name == nullptr) {
168 TLOGE("No name found for node with offset %d\n", mNodeOffset);
169 return Status::fromServiceSpecificError(
170 IDeviceTree::ERROR_NODE_NOT_FOUND);
171 }
172 *node_name = fdt_node_name;
173 return Status::ok();
174 }
175
get_subnode(const std::string & node_name,sp<INode> * node)176 Status Node::get_subnode(const std::string& node_name, sp<INode>* node) {
177 if (node == nullptr) {
178 TLOGE("Invalid arguments\n");
179 return Status::fromServiceSpecificError(
180 IDeviceTree::ERROR_INVALID_ARGS);
181 }
182
183 int subnode_offset =
184 ::fdt_subnode_offset(mDtb, mNodeOffset, node_name.c_str());
185 if (subnode_offset < 0) {
186 TLOGI("No subnode named %s was found\n", node_name.c_str());
187
188 // Other return codes indicate a dtb in an invalid state
189 assert(subnode_offset == -FDT_ERR_NOTFOUND);
190
191 return Status::fromServiceSpecificError(
192 IDeviceTree::ERROR_NODE_NOT_FOUND);
193 }
194 sp<Node> subnode = sp<Node>::make(subnode_offset, mDtb);
195 if (subnode == nullptr) {
196 TLOGE("Failed to allocate memory for Node\n");
197 return Status::fromServiceSpecificError(IDeviceTree::ERROR_NO_MEMORY);
198 }
199 *node = subnode;
200 return Status::ok();
201 }
202
get_subnodes(sp<INodeIterator> * node_iter)203 Status Node::get_subnodes(sp<INodeIterator>* node_iter) {
204 if (node_iter == nullptr) {
205 TLOGE("Invalid arguments\n");
206 return Status::fromServiceSpecificError(
207 IDeviceTree::ERROR_INVALID_ARGS);
208 }
209
210 int subnode_offset = ::fdt_first_subnode(mDtb, mNodeOffset);
211 if (subnode_offset < 0) {
212 TLOGI("No matching subnodes were found\n");
213
214 // Other return codes indicate a dtb in an invalid state
215 assert(subnode_offset == -FDT_ERR_NOTFOUND);
216
217 return Status::fromServiceSpecificError(
218 IDeviceTree::ERROR_NODE_NOT_FOUND);
219 }
220
221 sp<NodeIterator> iter = sp<NodeIterator>::make(subnode_offset, mDtb);
222 if (iter == nullptr) {
223 TLOGE("Failed to allocate memory for NodeIterator\n");
224 return Status::fromServiceSpecificError(IDeviceTree::ERROR_NO_MEMORY);
225 }
226 *node_iter = iter;
227
228 return Status::ok();
229 }
230
get_props(sp<IPropIterator> * prop_iter)231 Status Node::get_props(sp<IPropIterator>* prop_iter) {
232 if (prop_iter == nullptr) {
233 TLOGE("Invalid arguments\n");
234 return Status::fromServiceSpecificError(
235 IDeviceTree::ERROR_INVALID_ARGS);
236 }
237
238 int prop_offset = ::fdt_first_property_offset(mDtb, mNodeOffset);
239 if (prop_offset < 0) {
240 TLOGI("Node has no properties\n");
241
242 // Other return codes indicate a dtb in an invalid state
243 assert(prop_offset == -FDT_ERR_NOTFOUND);
244
245 return Status::fromServiceSpecificError(
246 IDeviceTree::ERROR_PROP_NOT_FOUND);
247 }
248
249 sp<PropIterator> iter = sp<PropIterator>::make(prop_offset, mDtb);
250 if (iter == nullptr) {
251 TLOGE("Failed to allocate memory for PropIterator\n");
252 return Status::fromServiceSpecificError(IDeviceTree::ERROR_NO_MEMORY);
253 }
254 *prop_iter = iter;
255
256 return Status::ok();
257 }
258
get_prop(const std::string & prop_name,Property * prop)259 Status Node::get_prop(const std::string& prop_name, Property* prop) {
260 if (prop == nullptr) {
261 TLOGE("Invalid arguments\n");
262 return Status::fromServiceSpecificError(
263 IDeviceTree::ERROR_INVALID_ARGS);
264 }
265
266 int len = -1;
267 const struct fdt_property* fdt_prop =
268 ::fdt_get_property(mDtb, mNodeOffset, prop_name.c_str(), &len);
269
270 if (len < 0) {
271 TLOGE("Node has no property named %s\n", prop_name.c_str());
272
273 // Other return codes indicate a dtb in an invalid state or an
274 // internal error in the device tree service
275 assert(len == -FDT_ERR_NOTFOUND);
276
277 return Status::fromServiceSpecificError(
278 IDeviceTree::ERROR_PROP_NOT_FOUND);
279 }
280
281 // This indicates an internal error in libfdt
282 if (fdt_prop == nullptr) {
283 return Status::fromServiceSpecificError(IDeviceTree::ERROR_GENERIC);
284 }
285
286 prop->value.resize(len);
287 prop->value.assign(fdt_prop->data, fdt_prop->data + len);
288
289 prop->name = prop_name;
290
291 return Status::ok();
292 }
293
get_next_prop(Property * prop)294 Status PropIterator::get_next_prop(Property* prop) {
295 if (prop == nullptr) {
296 TLOGE("Invalid arguments\n");
297 return Status::fromServiceSpecificError(
298 IDeviceTree::ERROR_INVALID_ARGS);
299 }
300
301 // If the current prop offset is -1, use the prop offset that was
302 // passed in to the constructor
303 if (mCurrentPropOffset == -1) {
304 mCurrentPropOffset = mInitialPropOffset;
305 } else {
306 // Advance current prop offset to the next prop
307 int next_prop = ::fdt_next_property_offset(mDtb, mCurrentPropOffset);
308 if (next_prop < 0) {
309 TLOGI("Reached the end of the property iterator\n");
310
311 // Other return codes indicate a dtb in an invalid state
312 assert(next_prop == -FDT_ERR_NOTFOUND);
313
314 return Status::fromServiceSpecificError(
315 IDeviceTree::ERROR_PROP_NOT_FOUND);
316 }
317 mCurrentPropOffset = next_prop;
318 }
319
320 int len = -1;
321 const struct fdt_property* fdt_prop =
322 ::fdt_get_property_by_offset(mDtb, mCurrentPropOffset, &len);
323
324 if (len < 0) {
325 return Status::fromServiceSpecificError(IDeviceTree::ERROR_GENERIC);
326 }
327
328 prop->value.resize(len);
329 prop->value.assign(fdt_prop->data, fdt_prop->data + len);
330
331 int prop_len;
332 const char* prop_name =
333 fdt_get_string(mDtb, fdt32_ld(&fdt_prop->nameoff), &prop_len);
334 if (prop_len < 0) {
335 return Status::fromServiceSpecificError(IDeviceTree::ERROR_GENERIC);
336 }
337 prop->name = std::string(prop_name, (size_t)prop_len);
338
339 return Status::ok();
340 }
341
342 } // namespace device_tree
343 } // namespace trusty
344 } // namespace android
345 } // namespace com
346