blob: a4430bed5852e3c87611ce024a3c32de159d48f5 [file] [log] [blame]
Sol Boucher69b88bf2015-02-26 11:47:19 -08001/*
2 * fmd.c, parser frontend and utility functions for flashmap descriptor language
3 *
4 * Copyright (C) 2015 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
18 */
19
20#include "fmd.h"
21
Sol Boucherb9740812015-03-18 10:13:48 -070022#include "common.h"
Sol Boucher69b88bf2015-02-26 11:47:19 -080023#include "fmd_parser.h"
24#include "fmd_scanner.h"
25#include "option.h"
26
27#include <assert.h>
28#include <search.h>
29#include <string.h>
30
31/*
32 * Validate the given flashmap descriptor node's properties. In particular:
33 * - Ensure its name is globally unique.
34 * - Ensure its offset, if known, isn't located before the end of the previous
35 * section, if this can be determined.
36 * - Ensure its offset, if known, isn't located after the beginning of the next
37 * section or off the end of its parent section, if this can be determined.
38 * - Ensure its size is nonzero.
39 * - Ensure that the combination of its size and offset, if they are both
40 * known, doesn't place its end after the beginning of the next section or
41 * off the end of its parent section, if this can be determined.
42 * In the case of a validation error, the particular problem is reported to
43 * standard error and this function returns false. It should be noted that this
44 * function makes no claim that the members of the node's child list are valid:
45 * under no circumstances is any recursive validation performed.
46 *
47 * @param node The flashmap descriptor node to be validated
48 * @param start Optional minimum permissible base of the section to be
49 * validated, to be provided if known
50 * @param end Optional maximum permissible offset to the end of the section to
51 * be validated, to be provided if known
52 * @return Whether the node is valid
53 */
54static bool validate_descriptor_node(const struct flashmap_descriptor *node,
55 struct unsigned_option start, struct unsigned_option end) {
56 assert(node);
57
58 ENTRY search_key = {node->name, NULL};
59 if (hsearch(search_key, FIND)) {
Sol Boucherb9740812015-03-18 10:13:48 -070060 ERROR("Multiple sections with name '%s'\n", node->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -080061 return false;
62 }
63 if (!hsearch(search_key, ENTER))
64 assert(false);
65
66 if (node->offset_known) {
67 if (start.val_known && node->offset < start.val) {
Sol Boucherb9740812015-03-18 10:13:48 -070068 ERROR("Section '%s' starts too low\n", node->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -080069 return false;
70 } else if (end.val_known && node->offset > end.val) {
Sol Boucherb9740812015-03-18 10:13:48 -070071 ERROR("Section '%s' starts too high\n", node->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -080072 return false;
73 }
74 }
75
76 if (node->size_known) {
77 if (node->size == 0) {
Sol Boucherb9740812015-03-18 10:13:48 -070078 ERROR("Section '%s' given no space\n", node->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -080079 return false;
80 } else if (node->offset_known) {
81 unsigned node_end = node->offset + node->size;
82 if (end.val_known && node_end > end.val) {
Sol Boucherb9740812015-03-18 10:13:48 -070083 ERROR("Section '%s' too big\n", node->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -080084 return false;
85 }
86 }
87 }
88
89 return true;
90}
91
92/*
93 * Performs reverse lateral processing of sibling nodes, as described by the
94 * documentation of its caller, validate_and_complete_info(). If it encounters
95 * a node that is invalid in a way that couldn't have been discovered earlier,
96 * it explains the problem to standard output and returns false.
97 *
98 * @param first_incomplete_it First node whose offset or size couldn't be
99 * determined during forward processing
100 * @param cur_incomplete_it Last node whose offset or size is unknown
101 * @param end_watermark Offset to the end of the unresolved region
102 * @return Whether all completed nodes were still valid
103 */
104static bool complete_missing_info_backward(
105 flashmap_descriptor_iterator_t first_incomplete_it,
106 flashmap_descriptor_iterator_t cur_incomplete_it,
107 unsigned end_watermark)
108{
109 assert(first_incomplete_it);
110 assert(cur_incomplete_it);
111 assert(cur_incomplete_it >= first_incomplete_it);
112
113 do {
114 struct flashmap_descriptor *cur = *cur_incomplete_it;
115
116 assert(cur->offset_known || cur->size_known);
117 if (!cur->offset_known) {
118 if (cur->size > end_watermark) {
Sol Boucherb9740812015-03-18 10:13:48 -0700119 ERROR("Section '%s' too big\n", cur->name);
Sol Boucher69b88bf2015-02-26 11:47:19 -0800120 return false;
121 }
122 cur->offset_known = true;
123 cur->offset = end_watermark -= cur->size;
124 } else if (!cur->size_known) {
125 if (cur->offset > end_watermark) {
Sol Boucherb9740812015-03-18 10:13:48 -0700126 ERROR("Section '%s' starts too high\n",
Sol Boucher69b88bf2015-02-26 11:47:19 -0800127 cur->name);
128 return false;
129 }
130 cur->size_known = true;
131 cur->size = end_watermark - cur->offset;
132 end_watermark = cur->offset;
133 }
134 } while (--cur_incomplete_it >= first_incomplete_it);
135
136 return true;
137}
138
139/*
140 * Recursively examine each descendant of the provided flashmap descriptor node
141 * to ensure its position and size are known, attempt to infer them otherwise,
142 * and validate their values once they've been populated.
143 * This processes nodes according to the following algorithm:
144 * - At each level of the tree, it moves laterally between siblings, keeping
145 * a watermark of its current offset relative to the previous section, which
146 * it uses to fill in any unknown offsets it encounters along the way.
147 * - The first time it encounters a sibling with unknown size, it loses track
148 * of the watermark, and is therefore unable to complete further offsets;
149 * instead, if the watermark was known before, it marks the current node as
150 * the first that couldn't be completed in the initial pass.
151 * - If the current watermark is unknown (i.e. a node has been marked as the
152 * first incomplete one) and one with a fixed offset is encountered, a
153 * reverse lateral traversal is dispatched that uses that provided offset as
154 * a reverse watermark to complete all unknown fields until it finishes with
155 * the node marked as the first incomplete one: at this point, that flag is
156 * cleared, the watermark is updated, and forward processing resumes from
157 * where it left off.
158 * - If the watermark is unknown (i.e. node(s) are incomplete) after traversing
159 * all children of a particular parent node, reverse processing is employed
160 * as described above, except that the reverse watermark is initialized to
161 * the parent node's size instead of the (nonexistent) next node's offset.
162 * - Once all of a node's children have been processed, the algorithm applies
163 * itself recursively to each of the child nodes; thus, lower levels of the
164 * tree are processed only after their containing levels are finished.
165 * This approach can fail in two possible ways (in which case the problem is
166 * reported to standard output and this function returns false):
167 * - Processing reveals that some node's provided value is invalid in some way.
168 * - Processing determines that one or more provided values require an omitted
169 * field to take a nonsensical value.
170 * - Processing determines that it is impossible to determine a group of
171 * omitted values. This state is detected when a node whose offset *and*
172 * value are omitted is encountered during forward processing and while the
173 * current watermark is unknown: in such a case, neither can be known without
174 * being provided with either the other or more context.
175 * The function notably performs neither validation nor completion on the parent
176 * node it is passed; thus, it is important to ensure that that node is valid.
177 * (At the very least, it must have a valid size field in order for the
178 * algorithm to work on its children.)
179 *
180 * @param cur_level Parent node, which must minimally already have a valid size
181 * @return Whether completing and validating the children succeeded
182 */
183static bool validate_and_complete_info(struct flashmap_descriptor *cur_level)
184{
185 assert(cur_level);
186 assert(cur_level->size_known);
187
188 // Our watermark is only known when first_incomplete_it is NULL.
189 flashmap_descriptor_iterator_t first_incomplete_it = NULL;
190 unsigned watermark = 0;
191
192 fmd_foreach_child_iterator(cur_it, cur_level) {
193 struct flashmap_descriptor *cur_section = *cur_it;
194
195 if (first_incomplete_it) {
196 if (cur_section->offset_known) {
197 if (complete_missing_info_backward(
198 first_incomplete_it, cur_it - 1,
199 cur_section->offset)) {
200 first_incomplete_it = NULL;
201 watermark = cur_section->offset;
202 } else {
203 return false;
204 }
205 }
206 // Otherwise, we can't go back until a provided offset.
207 } else if (!cur_section->offset_known) {
208 cur_section->offset_known = true;
209 cur_section->offset = watermark;
210 }
211
212 assert(cur_level->size_known);
213 struct unsigned_option max_endpoint = {true, cur_level->size};
214 if (cur_it != cur_level->list + cur_level->list_len - 1) {
215 struct flashmap_descriptor *next_section = cur_it[1];
216 max_endpoint.val_known = next_section->offset_known;
217 max_endpoint.val = next_section->offset;
218 }
219 if (!validate_descriptor_node(cur_section,
220 (struct unsigned_option)
221 {!first_incomplete_it, watermark},
222 max_endpoint))
223 return false;
224
225 if (!cur_section->size_known) {
226 if (!cur_section->offset_known) {
Sol Boucherb9740812015-03-18 10:13:48 -0700227 ERROR("Cannot determine either offset or size of section '%s'\n",
Sol Boucher69b88bf2015-02-26 11:47:19 -0800228 cur_section->name);
229 return false;
230 } else if (!first_incomplete_it) {
231 first_incomplete_it = cur_it;
232 } else {
233 // We shouldn't find an unknown size within an
234 // incomplete region because the backward
235 // traversal at the beginning of this node's
236 // processing should have concluded said region.
237 assert(!first_incomplete_it);
238 }
239 } else if (!first_incomplete_it) {
240 watermark = cur_section->offset + cur_section->size;
241 }
242 }
243
244 if (first_incomplete_it &&
245 !complete_missing_info_backward(first_incomplete_it,
246 cur_level->list + cur_level->list_len - 1,
247 cur_level->size))
248 return false;
249
250 fmd_foreach_child(cur_section, cur_level) {
251 assert(cur_section->offset_known);
252 assert(cur_section->size_known);
253
254 if (!validate_and_complete_info(cur_section))
255 return false;
256 }
257
258 return true;
259}
260
261static void print_with_prefix(const struct flashmap_descriptor *tree,
262 const char *pre)
263{
264 assert(tree);
265 assert(pre);
266
267 printf("%ssection '%s' has ", pre, tree->name);
268
269 if (tree->offset_known)
270 printf("offset %uB, ", tree->offset);
271 else
272 fputs("unknown offset, ", stdout);
273
274 if (tree->size_known)
275 printf("size %uB, ", tree->size);
276 else
277 fputs("unknown size, ", stdout);
278
279 printf("and %zu subsections", tree->list_len);
280 if (tree->list_len) {
281 puts(":");
282
283 char child_prefix[strlen(pre) + 1];
284 strcpy(child_prefix, pre);
285 strcat(child_prefix, "\t");
286 fmd_foreach_child(each, tree)
287 print_with_prefix(each, child_prefix);
288 } else {
289 puts("");
290 }
291}
292
293struct flashmap_descriptor *fmd_create(FILE *stream)
294{
295 assert(stream);
296
297 yyin = stream;
298
299 struct flashmap_descriptor *ret = NULL;
300 if (yyparse() == 0)
301 ret = res;
302
303 yylex_destroy();
304 yyin = NULL;
305 res = NULL;
306
307 if (ret) {
308 // This hash table is used to store the declared name of each
309 // section and ensure that each is globally unique.
310 if (!hcreate(fmd_count_nodes(ret))) {
Sol Boucherb9740812015-03-18 10:13:48 -0700311 perror("E: While initializing hashtable");
Sol Boucher69b88bf2015-02-26 11:47:19 -0800312 fmd_cleanup(ret);
313 return NULL;
314 }
315
316 // Even though we haven't checked that the root node (ret) has
317 // a size field as required by this function, the parser
318 // warrants that it does because the grammar requires it.
319 if (!validate_and_complete_info(ret)) {
320 hdestroy();
321 fmd_cleanup(ret);
322 return NULL;
323 }
324
325 hdestroy();
326 }
327
328 return ret;
329}
330
331void fmd_cleanup(struct flashmap_descriptor *victim)
332{
333 if (!victim)
334 return;
335
336 free(victim->name);
337 for (unsigned idx = 0; idx < victim->list_len; ++idx)
338 fmd_cleanup(victim->list[idx]);
339 free(victim->list);
340 free(victim);
341}
342
343size_t fmd_count_nodes(const struct flashmap_descriptor *tree)
344{
345 assert(tree);
346
347 if (!tree->list_len)
348 return 1;
349
350 unsigned count = 1;
351 fmd_foreach_child(lower, tree)
352 count += fmd_count_nodes(lower);
353 return count;
354}
355
356const struct flashmap_descriptor *fmd_find_node(
357 const struct flashmap_descriptor *root, const char *name)
358{
359 assert(root);
360 assert(name);
361
362 if (strcmp(root->name, name) == 0)
363 return root;
364
365 fmd_foreach_child(descendant, root) {
366 const struct flashmap_descriptor *match =
367 fmd_find_node(descendant, name);
368 if (match)
369 return match;
370 }
371 return NULL;
372}
373
374unsigned fmd_calc_absolute_offset(const struct flashmap_descriptor *root,
375 const char *name)
376{
377 assert(root);
378 assert(name);
379
380 if (strcmp(root->name, name) == 0)
381 return 0;
382
383 fmd_foreach_child(descendant, root) {
384 unsigned subtotal = fmd_calc_absolute_offset(descendant, name);
385 if (subtotal != FMD_NOTFOUND)
386 return descendant->offset + subtotal;
387 }
388 return FMD_NOTFOUND;
389}
390
391void fmd_print(const struct flashmap_descriptor *tree)
392{
393 print_with_prefix(tree, "");
394}