1 /*
2  * Copyright (c) 2022 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <inttypes.h>
25 #include <lib/dtb_embedded/dtb_embedded.h>
26 #include <libfdt.h>
27 #include <lk/compiler.h>
28 #include <lk/macros.h>
29 #include <lk/trace.h>
30 #include <stdint.h>
31 #include <uapi/uapi/err.h>
32 
33 #define LOCAL_TRACE (0)
34 
35 extern char __trusty_dtb_start;
36 extern char __trusty_dtb_end;
37 
38 struct dtb_embedded_iterator {
39     uintptr_t offset;
40 };
41 
dtb_embedded_iterator_reset(struct dtb_embedded_iterator * iter)42 void dtb_embedded_iterator_reset(struct dtb_embedded_iterator* iter) {
43     if (iter) {
44         iter->offset = 0;
45     }
46 }
47 
dtb_embedded_iterator_new(struct dtb_embedded_iterator ** piter)48 int dtb_embedded_iterator_new(struct dtb_embedded_iterator** piter) {
49     ASSERT(piter);
50 
51     struct dtb_embedded_iterator* iter = (struct dtb_embedded_iterator*)calloc(
52             1, sizeof(struct dtb_embedded_iterator));
53     if (!iter) {
54         TRACEF("failed to allocate iterator\n");
55         return ERR_NO_MEMORY;
56     }
57 
58     dtb_embedded_iterator_reset(iter);
59     *piter = iter;
60 
61     return NO_ERROR;
62 }
63 
dtb_embedded_iterator_free(struct dtb_embedded_iterator ** piter)64 void dtb_embedded_iterator_free(struct dtb_embedded_iterator** piter) {
65     ASSERT(piter);
66 
67     if (*piter) {
68         free(*piter);
69         *piter = NULL;
70     }
71 }
72 
dtb_embedded_iterator_next(struct dtb_embedded_iterator * iter,const void ** dtb,size_t * dtb_size)73 int dtb_embedded_iterator_next(struct dtb_embedded_iterator* iter,
74                                const void** dtb,
75                                size_t* dtb_size) {
76     if (!iter) {
77         TRACEF("Invalid iterator\n");
78         return ERR_INVALID_ARGS;
79     }
80     if (!dtb) {
81         TRACEF("Invalid dtb pointer\n");
82         return ERR_INVALID_ARGS;
83     }
84     if (!dtb_size) {
85         TRACEF("Invalid dtb size pointer\n");
86         return ERR_INVALID_ARGS;
87     }
88 
89     /* Check that the iterator is within the embedded dtb range */
90     uintptr_t end = (uintptr_t)&__trusty_dtb_end;
91     uintptr_t start = (uintptr_t)&__trusty_dtb_start;
92     uintptr_t total_size = end - start;
93     if (iter->offset >= total_size) {
94         TRACEF("Embedded dtb iterator at offset %" PRIuPTR "out of range\n",
95                iter->offset);
96         return ERR_OUT_OF_RANGE;
97     }
98 
99     /* Validate the header of the next dtb */
100     const void* dtb_start = (const void*)(start + iter->offset);
101     int rc = fdt_check_header(dtb_start);
102     if (rc < 0) {
103         TRACEF("Embedded dtb at offset %" PRIuPTR
104                " has an invalid header (%d)\n",
105                iter->offset, rc);
106         return ERR_BAD_STATE;
107     }
108 
109     /*
110      * Check that the dtb size is in the expected range. Each dtb's size is read
111      * from its header plus 4 bytes from the symbol size inserted by
112      * INCBIN_ALIGNED. Then we round up to 8 bytes since each individual dtb is
113      * 8-byte aligned.
114      */
115     const size_t dtb_alignment = 8;
116     const size_t sym_size = 4;
117     size_t next_dtb_size =
118             round_up(fdt_totalsize(dtb_start) + sym_size, dtb_alignment);
119     if (next_dtb_size + iter->offset > total_size) {
120         TRACEF("Embedded dtb at offset %" PRIuPTR " has invalid size %zu\n",
121                iter->offset, next_dtb_size);
122         return ERR_BAD_LEN;
123     }
124     iter->offset += next_dtb_size;
125     *dtb = dtb_start;
126     *dtb_size = next_dtb_size;
127     LTRACEF("Found embedded dtb at offset %" PRIuPTR "\n", iter->offset);
128     return NO_ERROR;
129 }
130