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