summaryrefslogtreecommitdiff
path: root/tests/libufdt_verify/ufdt_test_overlay.cpp
blob: 2554d9df6ac4f36ce15bc6afa6b5c1c2f65009ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>
#include <unordered_map>

extern "C" {

#include "libufdt.h"
#include "ufdt_node_pool.h"
#include "ufdt_overlay.h"
#include "ufdt_overlay_internal.h"

}

#include "ufdt_test_overlay.h"

static bool ufdt_node_compare(struct ufdt_node *node_a, struct ufdt_node *node_b,
                              struct ufdt* tree_a, struct ufdt* tree_b);

/*
 * Helper method to check if the tree rooted at node_b is a subset of the tree rooted
 * at node_a.
 */
static bool compare_child_nodes(struct ufdt_node *node_a, struct ufdt_node *node_b,
                                struct ufdt * tree_a, struct ufdt * tree_b) {
    bool result = true;
    struct ufdt_node *it;

    for (it = ((struct ufdt_node_fdt_node *)node_b)->child; it; it = it->sibling) {
        struct ufdt_node *cur_node = it;
        struct ufdt_node *target_node = NULL;

        if (ufdt_node_tag(cur_node) == FDT_BEGIN_NODE) {
            target_node =
                    ufdt_node_get_subnode_by_name(node_a, ufdt_node_name(cur_node));
        } else {
            target_node =
                    ufdt_node_get_property_by_name(node_a, ufdt_node_name(cur_node));
        }

        if (target_node == NULL) {
            result = false;
        } else {
            result = ufdt_node_compare(target_node, cur_node, tree_a, tree_b);
        }

        if (!result) {
            break;
        }
    }

    return result;
}

/*
 * Method to compare two nodes with tag FDT_PROP. Also accounts for the cases where
 * the property type is phandle.
 */
static bool ufdt_compare_property(struct ufdt_node* node_final, struct ufdt_node* node_overlay,
                                  struct ufdt* tree_final, struct ufdt* tree_overlay) {
    if (ufdt_node_tag(node_final) == FDT_PROP) {
        /* Return -1 if property names are differemt */
        if (strcmp(ufdt_node_name(node_final), ufdt_node_name(node_overlay)) != 0)
            return false;

        int length_data_final = 0, length_data_overlay = 0;
        char *prop_data_final = ufdt_node_get_fdt_prop_data(node_final, &length_data_final);
        char *prop_data_overlay = ufdt_node_get_fdt_prop_data(node_overlay,
                                                              &length_data_overlay);

        /* Confirm length for the property values are the same */
        if (length_data_final != length_data_overlay) {
            return false;
        }

        if (((length_data_final == 0) && (length_data_overlay ==0)) ||
            (memcmp(prop_data_final, prop_data_overlay, length_data_final) == 0)) {
            // Return if the properties have same value.
            return true;
        } else {
            /* check for the presence of phandles */
            for (int i = 0; i < length_data_final; i += sizeof(fdt32_t)) {
                int offset_data_a = fdt32_to_cpu(
                        *reinterpret_cast<fdt32_t *>(prop_data_final + i));
                int offset_data_b = fdt32_to_cpu(
                        *reinterpret_cast<fdt32_t *>(prop_data_overlay + i));
                if (offset_data_a == offset_data_b) continue;
                /* If the offsets have phandles, they would have valid target nodes */
                struct ufdt_node * target_node_a = ufdt_get_node_by_phandle(tree_final,
                                                                            offset_data_a);
                struct ufdt_node * target_node_b = ufdt_get_node_by_phandle(tree_overlay,
                                                                            offset_data_b);

                /*
                 * verify that the target nodes are valid and point to the same node.
                 */
                if ((target_node_a == NULL) || (target_node_b == NULL) ||
                    strcmp(ufdt_node_name(target_node_a),
                           ufdt_node_name(target_node_b)) != 0) {
                    return false;
                }
            }
        }
    }

    return true;
}

/*
 * Checks if the ufdt tree rooted at node_b is a subtree of the tree rooted at
 * node_a.
 */
static bool ufdt_node_compare(struct ufdt_node *node_final, struct ufdt_node *node_overlay,
                              struct ufdt * tree_final, struct ufdt * tree_overlay) {
    if (ufdt_node_tag(node_final) == FDT_PROP) {
        return ufdt_compare_property(node_final, node_overlay, tree_final, tree_overlay);
    }

    return compare_child_nodes(node_final, node_overlay, tree_final, tree_overlay);
}


/*
 * Multiple fragments may fixup to the same node on the base device tree.
 * Combine these fragments for easier verification.
 */
void ufdt_combine_fixup(struct ufdt *tree, const char *fixup,
                        struct ufdt_node **prev_node, struct ufdt_node_pool *node_pool) {
    char *path, *prop_ptr, *offset_ptr;
    char path_buf[1024];
    char *path_mem = NULL;
    int result = 0;

    size_t fixup_len = strlen(fixup) + 1;
    if (fixup_len > sizeof(path_buf)) {
        path_mem = static_cast<char *>(dto_malloc(fixup_len));
        path = path_mem;
    } else {
        path = path_buf;
    }
    dto_memcpy(path, fixup, fixup_len);

    prop_ptr = dto_strchr(path, ':');
    if (prop_ptr == NULL) {
        dto_error("Missing property part in '%s'\n", path);
        goto fail;
    }

    *prop_ptr = '\0';
    prop_ptr++;

    offset_ptr = dto_strchr(prop_ptr, ':');
    if (offset_ptr == NULL) {
        dto_error("Missing offset part in '%s'\n", path);
        goto fail;
    }

    *offset_ptr = '\0';
    offset_ptr++;

    result = dto_strcmp(prop_ptr, "target");
    /* If the property being fixed up is not target, ignore and return */
    if (result == 0) {
        struct ufdt_node *target_node;
        target_node = ufdt_get_node_by_path(tree, path);
        if (target_node == NULL) {
            dto_error("Path '%s' not found\n", path);
        } else {
            /* The goal is to combine fragments that have a common target */
            if (*prev_node != NULL) {
                ufdt_node_merge_into(*prev_node, target_node, node_pool);
            } else {
                *prev_node = target_node;
            }
        }
    }

fail:
    if (path_mem) {
        dto_free(path_mem);
    }

    return;
}

/*
 * Creates a table of node paths to their corresponding phandles by walking
 * through the 'symbols' node of the main device tree. The table would be
 * used in combining overlay nodes that map to the same nodes in the
 * main device tree.
 */
void create_path_phandle_map(std::unordered_map<uint32_t, std::string>* phandle_path_map,
                             struct ufdt* main_tree) {
    int len = 0;
    struct ufdt_node *main_symbols_node =
            ufdt_get_node_by_path(main_tree, "/__symbols__");
    if (!main_symbols_node) {
        dto_error("No node __symbols__ in main dtb.\n");
        return;
    }

    struct ufdt_node **it = nullptr;
    for_each_prop(it, main_symbols_node) {
        const char* symbol_path = ufdt_node_get_fdt_prop_data(*it, &len);
        struct ufdt_node* symbol_node = ufdt_get_node_by_path(main_tree, symbol_path);
        uint32_t phandle = ufdt_node_get_phandle(symbol_node);
        (*phandle_path_map)[phandle] = std::string(symbol_path);
    }
}

/*
 * Recursively checks whether a node from another overlay fragment had overlaid the
 * target node and if so merges the node into the previous node.
 */
static void combine_overlay_node(std::unordered_map<std::string,
                                 struct ufdt_node*>* path_node_map,
                                 std::string path,
                                 struct ufdt_node* node,
                                 struct ufdt_node_pool* pool) {
    struct ufdt_node **it = nullptr;
    for_each_node(it, node) {
        //skips properties
        if (ufdt_node_tag(*it) == FDT_BEGIN_NODE) {
            combine_overlay_node(path_node_map, path + "/" + ufdt_node_name(*it), *it, pool);
        }
    }

    if (path_node_map->find(path) != path_node_map->end()) {
        ufdt_node_merge_into((*path_node_map)[path], node, pool);
    } else {
        //This is the first node overlaying the target node, add the same to the
        //table.
        (*path_node_map)[path] = node;
    }
}

/* END of doing fixup in the overlay ufdt. */

static bool ufdt_verify_overlay_node(struct ufdt_node *target_node,
                                     struct ufdt_node *overlay_node,
                                     struct ufdt * target_tree,
                                     struct ufdt * overlay_tree) {
    return ufdt_node_compare(target_node, overlay_node, target_tree, overlay_tree);
}

/*
 * verify one overlay fragment (subtree).
 */
static int ufdt_verify_fragment(struct ufdt *tree,
                                struct ufdt *overlay_tree,
                                struct ufdt_node *frag_node) {
    struct ufdt_node *target_node = NULL;
    struct ufdt_node *overlay_node = NULL;
    enum overlay_result target_search_result = ufdt_overlay_get_target(tree, frag_node,
                                                                       &target_node);
    if (target_node == NULL) {
        return target_search_result;
    }

    overlay_node = ufdt_node_get_node_by_path(frag_node, "__overlay__");
    if (overlay_node == NULL) {
        dto_error("missing __overlay__ sub-node\n");
        return OVERLAY_RESULT_MISSING_OVERLAY;
    }

    bool result = ufdt_verify_overlay_node(target_node, overlay_node, tree, overlay_tree);

    if (!result) {
        dto_error("failed to verify overlay node %s to target %s\n",
                  ufdt_node_name(overlay_node), ufdt_node_name(target_node));
        return OVERLAY_RESULT_VERIFY_FAIL;
    }

    return OVERLAY_RESULT_OK;
}

/*
 * verify each fragment in overlay.
 */
static int ufdt_overlay_verify_fragments(struct ufdt *final_tree,
                                         struct ufdt *overlay_tree) {
    enum overlay_result err;
    struct ufdt_node **it;
    for_each_node(it, overlay_tree->root) {
        err = static_cast<enum overlay_result>(ufdt_verify_fragment(final_tree, overlay_tree,
                                                                    *it));
        if (err == OVERLAY_RESULT_VERIFY_FAIL) {
            return -1;
        }
    }
    return 0;
}

/*
 * Examine target nodes for fragments in all overlays and combine ones with the
 * same target.
 */
static void ufdt_overlay_combine_common_nodes(struct ufdt** overlay_trees,
                                              size_t overlay_count,
                                              struct ufdt* final_tree,
                                              struct ufdt_node_pool* pool
                                             ) {
    std::unordered_map<std::string, struct ufdt_node*> path_node_map;
    std::unordered_map<uint32_t, std::string> phandle_path_map;

    create_path_phandle_map(&phandle_path_map, final_tree);

    struct ufdt_node **it = nullptr;
    for (size_t i = 0; i < overlay_count; i++) {
        for_each_node(it, overlay_trees[i]->root) {
            uint32_t target = 0;
            const void* val = ufdt_node_get_fdt_prop_data_by_name(*it, "target", NULL);
            if (val) {
                dto_memcpy(&target, val, sizeof(target));
                target = fdt32_to_cpu(target);
                std::string path = phandle_path_map[target];
                struct ufdt_node* overlay_node = ufdt_node_get_node_by_path(*it, "__overlay__");
                if (overlay_node != nullptr) {
                    combine_overlay_node(&path_node_map, path, overlay_node, pool);
                }
            }
        }
    }
}

/*
 * Makes sure that all phandles in the overlays are unique since they will be
 * combined before verification.
 */
int ufdt_resolve_duplicate_phandles(ufdt** overlay_tree, size_t overlay_count) {
  size_t phandle_offset = 0;
  for (size_t i = 0; i < overlay_count; i++) {
        ufdt_try_increase_phandle(overlay_tree[i], phandle_offset);
        if (ufdt_overlay_do_local_fixups(overlay_tree[i], phandle_offset) < 0) {
            return -1;
        }
        phandle_offset = ufdt_get_max_phandle(overlay_tree[i]);
  }

  return 0;
}

/*
 * Combines all overlays into a single tree at overlay_trees[0]
 */
int ufdt_combine_all_overlays(struct ufdt** overlay_trees, size_t overlay_count,
                              struct ufdt* final_tree, struct ufdt_node_pool* pool) {
    struct ufdt* combined_overlay_tree = nullptr;

    if (!overlay_trees || !overlay_count || !final_tree || !pool) {
        return -1;
    }

    /*
     * If there are duplicate phandles amongst the overlays, replace them with
     * unique ones.
     */
    if (ufdt_resolve_duplicate_phandles(overlay_trees, overlay_count) < 0) {
        return -1;
    }

    /*
     * For each overlay, perform fixup for each fragment.
     */
    for (size_t i = 0; i < overlay_count; i++) {
        if (ufdt_overlay_do_fixups(final_tree, overlay_trees[i]) < 0) {
            dto_error("failed to perform fixups in overlay\n");
            return -1;
        }
    }

    /*
     * Iterate through each overlay and combine all nodes with the same target
     * node.
     */
    ufdt_overlay_combine_common_nodes(overlay_trees, overlay_count, final_tree, pool);

    /*
     * Combine all overlays into the tree at overlay_trees[0] for easy
     * verification.
     */
    combined_overlay_tree = overlay_trees[0];
    struct ufdt_node* combined_root_node = combined_overlay_tree->root;

    for (size_t i = 1; i < overlay_count; i++) {
        struct ufdt_node** it = nullptr;
        struct ufdt_node* root_node = overlay_trees[i]->root;
        for_each_node(it, root_node) {
            ufdt_node_add_child(combined_root_node, *it);
        }
        ((struct ufdt_node_fdt_node *)root_node)->child = nullptr;
    }

    /*
     * Rebuild the phandle_table for the combined tree.
     */
    combined_overlay_tree->phandle_table = build_phandle_table(combined_overlay_tree);

    return 0;
}

int ufdt_verify_dtbo(struct fdt_header* final_fdt_header,
                     size_t final_fdt_size, void** overlay_arr,
                     size_t overlay_count) {
    const size_t min_fdt_size = 8;
    struct ufdt_node_pool pool;
    struct ufdt* final_tree = nullptr;
    struct ufdt** overlay_trees = nullptr;
    int result = 1;

    if (final_fdt_header == NULL) {
        goto fail;
    }

    if (final_fdt_size < min_fdt_size || final_fdt_size != fdt_totalsize(final_fdt_header)) {
        dto_error("Bad fdt size!\n");
        goto fail;
    }

    for (size_t i = 0; i < overlay_count; i++) {
        if ((fdt_magic(overlay_arr[i]) != FDT_MAGIC) || !fdt_totalsize(overlay_arr[i])) {
            dto_error("Corrupted or empty overlay\n");
            goto fail;
        }
    }
    ufdt_node_pool_construct(&pool);
    final_tree = ufdt_from_fdt(final_fdt_header, final_fdt_size, &pool);

    overlay_trees = new ufdt*[overlay_count];
    for (size_t i = 0; i < overlay_count; i++) {
        size_t fdt_size = fdt_totalsize(overlay_arr[i]);
        overlay_trees[i] = ufdt_from_fdt(overlay_arr[i], fdt_size, &pool);
    }

    if (ufdt_combine_all_overlays(overlay_trees, overlay_count, final_tree, &pool) < 0) {
        dto_error("Unable to combine overlays\n");
        goto fail;
    }
    if (ufdt_overlay_verify_fragments(final_tree, overlay_trees[0]) < 0) {
        dto_error("Failed to verify overlay application\n");
        goto fail;
    } else {
        result = 0;
    }

fail:
    if (overlay_trees) {
        for (size_t i = 0; i < overlay_count; i++) {
            ufdt_destruct(overlay_trees[i], &pool);
        }
        delete[] overlay_trees;
    }

    ufdt_destruct(final_tree, &pool);
    ufdt_node_pool_destruct(&pool);
    return result;
}