1/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 *     Redistribution and use in source and binary forms, with or
6 *     without modification, are permitted provided that the following
7 *     conditions are met:
8 *
9 *     1. Redistributions of source code must retain the above
10 *        copyright notice, this list of conditions and the following
11 *        disclaimer.
12 *     2. Redistributions in binary form must reproduce the above
13 *        copyright notice, this list of conditions and the following
14 *        disclaimer in the documentation and/or other materials
15 *        provided with the distribution.
16 *
17 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
18 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <fdt.h>
32#include <libfdt.h>
33
34#include "libfdt_env.h"
35#include "libfdt_internal.h"
36
37int fdt_check_header(const void *fdt)
38{
39        if (fdt_magic(fdt) == FDT_MAGIC) {
40                /* Complete tree */
41                if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
42                        return -FDT_ERR_BADVERSION;
43                if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
44                        return -FDT_ERR_BADVERSION;
45        } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
46                /* Unfinished sequential-write blob */
47                if (fdt_size_dt_struct(fdt) == 0)
48                        return -FDT_ERR_BADSTATE;
49        } else {
50                return -FDT_ERR_BADMAGIC;
51        }
52
53        return 0;
54}
55
56const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
57{
58        const char *p;
59
60        if (fdt_version(fdt) >= 0x11)
61                if (((offset + len) < offset)
62                    || ((offset + len) > fdt_size_dt_struct(fdt)))
63                        return NULL;
64
65        p = _fdt_offset_ptr(fdt, offset);
66
67        if (p + len < p)
68                return NULL;
69        return p;
70}
71
72uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
73{
74        const fdt32_t *tagp, *lenp;
75        uint32_t tag;
76        int offset = startoffset;
77        const char *p;
78
79        *nextoffset = -FDT_ERR_TRUNCATED;
80        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
81        if (!tagp)
82                return FDT_END; /* premature end */
83        tag = fdt32_to_cpu(*tagp);
84        offset += FDT_TAGSIZE;
85
86        *nextoffset = -FDT_ERR_BADSTRUCTURE;
87        switch (tag) {
88        case FDT_BEGIN_NODE:
89                /* skip name */
90                do {
91                        p = fdt_offset_ptr(fdt, offset++, 1);
92                } while (p && (*p != '\0'));
93                if (!p)
94                        return FDT_END; /* premature end */
95                break;
96
97        case FDT_PROP:
98                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
99                if (!lenp)
100                        return FDT_END; /* premature end */
101                /* skip-name offset, length and value */
102                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
103                        + fdt32_to_cpu(*lenp);
104                break;
105
106        case FDT_END:
107        case FDT_END_NODE:
108        case FDT_NOP:
109                break;
110
111        default:
112                return FDT_END;
113        }
114
115        if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
116                return FDT_END; /* premature end */
117
118        *nextoffset = FDT_TAGALIGN(offset);
119        return tag;
120}
121
122int _fdt_check_node_offset(const void *fdt, int offset)
123{
124        if ((offset < 0) || (offset % FDT_TAGSIZE)
125            || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
126                return -FDT_ERR_BADOFFSET;
127
128        return offset;
129}
130
131int _fdt_check_prop_offset(const void *fdt, int offset)
132{
133        if ((offset < 0) || (offset % FDT_TAGSIZE)
134            || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
135                return -FDT_ERR_BADOFFSET;
136
137        return offset;
138}
139
140int fdt_next_node(const void *fdt, int offset, int *depth)
141{
142        int nextoffset = 0;
143        uint32_t tag;
144
145        if (offset >= 0)
146                if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
147                        return nextoffset;
148
149        do {
150                offset = nextoffset;
151                tag = fdt_next_tag(fdt, offset, &nextoffset);
152
153                switch (tag) {
154                case FDT_PROP:
155                case FDT_NOP:
156                        break;
157
158                case FDT_BEGIN_NODE:
159                        if (depth)
160                                (*depth)++;
161                        break;
162
163                case FDT_END_NODE:
164                        if (depth && ((--(*depth)) < 0))
165                                return nextoffset;
166                        break;
167
168                case FDT_END:
169                        if ((nextoffset >= 0)
170                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
171                                return -FDT_ERR_NOTFOUND;
172                        else
173                                return nextoffset;
174                }
175        } while (tag != FDT_BEGIN_NODE);
176
177        return offset;
178}
179
180const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
181{
182        int len = strlen(s) + 1;
183        const char *last = strtab + tabsize - len;
184        const char *p;
185
186        for (p = strtab; p <= last; p++)
187                if (memcmp(p, s, len) == 0)
188                        return p;
189        return NULL;
190}
191
192int fdt_move(const void *fdt, void *buf, int bufsize)
193{
194        FDT_CHECK_HEADER(fdt);
195
196        if (fdt_totalsize(fdt) > bufsize)
197                return -FDT_ERR_NOSPACE;
198
199        memmove(buf, fdt, fdt_totalsize(fdt));
200        return 0;
201}
202