blob: c43d80528b43475bc02a728cae85c5cb5bdb65c2 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Brian Swetland
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 "devicetree.h"
25
26#define DT_MAGIC 0xD00DFEED
27#define DT_NODE_BEGIN 1
28#define DT_NODE_END 2
29#define DT_PROP 3
30#define DT_END 9
31
32typedef struct dt_slice slice_t;
33
34u32 dt_rd32(u8 *data) {
35 return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
36}
37
38void dt_wr32(u32 n, u8 *data) {
39 *data++ = n >> 24;
40 *data++ = n >> 16;
41 *data++ = n >> 8;
42 *data = n;
43}
44
45/* init subslice from slice, returning 0 if successful */
46static int sslice(slice_t *src, slice_t *dst, u32 off, u32 len) {
47 if (off >= src->size)
48 return -1;
49 if (len >= src->size)
50 return -1;
51 if ((off + len) > src->size)
52 return -1;
53 dst->data = src->data + off;
54 dst->size = len;
55 return 0;
56}
57
58/* return nonzero if slice is empty */
59static inline int sempty(slice_t *s) {
60 return s->size == 0;
61}
62
63/* read be32 from slice,
64 * or 0 (and make slice empty) if slice is too small
65 */
66static u32 su32(slice_t *s) {
67 if (s->size < 4) {
68 s->size = 0;
69 return 0;
70 } else {
71 u32 n = (s->data[0] << 24) | (s->data[1] << 16) | (s->data[2] << 8) | s->data[3];
72 s->size -= 4;
73 s->data += 4;
74 return n;
75 }
76}
77
78/* return pointer to data in slice,
79 * or 0 (and make slice empty) if slice is too small
80 */
81static void *sdata(slice_t *s, u32 len) {
82 if (len > s->size) {
83 s->size = 0;
84 return 0;
85 } else {
86 void *data = s->data;
87 s->size -= len;
88 s->data += len;
89 while (len & 3) {
90 if (s->size) {
91 s->size++;
92 s->data++;
93 }
94 len++;
95 }
96 return data;
97 }
98}
99
100/* return pointer to string in slice,
101 * or "" (and make slice empty) if slice is too small
102 */
103static const char *sstring(slice_t *s) {
104 u32 sz = s->size;
105 u8 *end = s->data;
106 const char *data;
107 while (sz-- > 0) {
108 if (*end++ == 0) {
109 while (((end - s->data) & 3) && (sz > 0)) {
110 end++;
111 sz--;
112 }
113 data = (const char*) s->data;
114 s->size = sz;
115 s->data = end;
116 return data;
117 }
118 }
119 s->size = 0;
120 return "";
121}
122
123static int oops(devicetree_t *dt, const char *msg) {
124 if (dt->error)
125 dt->error(msg);
126 return -1;
127}
128
129int dt_init(devicetree_t *dt, void *data, u32 len) {
130 slice_t s;
131
132 dt->top.data = data;
133 dt->top.size = len;
134
135 s = dt->top;
136
137 dt->hdr.magic = su32(&s);
138 dt->hdr.size = su32(&s);
139 dt->hdr.off_struct = su32(&s);
140 dt->hdr.off_strings = su32(&s);
141 dt->hdr.off_reserve = su32(&s);
142 dt->hdr.version = su32(&s);
143 dt->hdr.version_compat = su32(&s);
144 dt->hdr.boot_cpuid = su32(&s);
145 dt->hdr.sz_strings = su32(&s);
146 dt->hdr.sz_struct = su32(&s);
147
148 if (dt->hdr.magic != DT_MAGIC)
149 return oops(dt, "bad magic");
150 if (dt->hdr.size > dt->top.size)
151 return oops(dt, "bogus size field");
152 if (dt->hdr.version != 17)
153 return oops(dt, "version != 17");
154 if (sslice(&dt->top, &dt->dt, dt->hdr.off_struct, dt->hdr.sz_struct))
155 return oops(dt, "invalid structure off/len");
156 if (sslice(&dt->top, &dt->ds, dt->hdr.off_strings, dt->hdr.sz_strings))
157 return oops(dt, "invalid strings off/len");
158
159 return 0;
160}
161
162int dt_walk(devicetree_t *dtree, dt_node_cb ncb, dt_prop_cb pcb, void *cookie) {
163 const char *p;
164 void *data;
165 u32 depth = 0;
166 slice_t dt, ds;
167 u32 sz, str;
168
169 dt = dtree->dt;
170 ds = dtree->ds;
171
172 while (!sempty(&dt)) {
173 u32 type = su32(&dt);
174 switch (type) {
175 case DT_END:
176 if (depth)
177 return oops(dtree, "unexpected DT_END");
178 return 0;
179 case DT_NODE_BEGIN:
180 depth++;
181 p = sstring(&dt);
182 if (ncb(depth, p, cookie))
183 return 0;
184 break;
185 case DT_NODE_END:
186 if (depth == 0)
187 return oops(dtree, "unexpected NODE_END");
188 depth--;
189 break;
190 case DT_PROP:
191 if (depth == 0)
192 return oops(dtree, "PROP outside of NODE");
193 sz = su32(&dt);
194 str = su32(&dt);
195 data = sdata(&dt, sz);
196 if (pcb((const char*) (ds.data + str), data, sz, cookie))
197 return 0;
198 break;
199 default:
200 return oops(dtree, "invalid node type");
201
202 }
203 }
204
205 if (depth != 0)
206 return oops(dtree, "incomplete tree");
207
208 return 0;
209}