blob: e52648a665a766b7941a5a5eb7a59cbd43d63a4c [file] [log] [blame]
Peer Chen8d782ee2011-01-18 21:34:18 -05001/**
2 * Copyright (c) 2011 NVIDIA Corporation. All rights reserved.
3 *
4 * See file CREDITS for list of people who contributed to this
5 * project.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23/*
24 * parse.c - Parsing support for the cbootimage tool
25 */
26
27/*
28 * TODO / Notes
29 * - Add doxygen commentary
30 * - Do we have endian issues to deal with?
31 * - Add support for device configuration data
32 * - Add support for bad blocks
33 * - Add support for different allocation modes/strategies
34 * - Add support for multiple BCTs in journal block
35 * - Add support for other missing features.
36 */
37
38#include "parse.h"
39#include "cbootimage.h"
40#include "data_layout.h"
41#include "crypto.h"
42#include "set.h"
43
44/*
45 * Function prototypes
46 *
47 * ParseXXX() parses XXX in the input
48 * SetXXX() sets state based on the parsing results but does not perform
49 * any parsing of its own
50 * A ParseXXX() function may call other parse functions and set functions.
51 * A SetXXX() function may not call any parseing functions.
52 */
53
54static char *parse_u32(char *statement, u_int32_t *val);
55static char *parse_filename(char *statement, char *name, int chars_remaining);
56static int
57parse_array(build_image_context *context, parse_token token, char *rest);
58static int
59parse_bootloader(build_image_context *context, parse_token token, char *rest);
60static int
61parse_value_u32(build_image_context *context, parse_token token, char *rest);
62static int
63parse_bct_file(build_image_context *context, parse_token token, char *rest);
64static int
65parse_addon(build_image_context *context, parse_token token, char *rest);
66static char *parse_string(char *statement, char *uname, int chars_remaining);
67static char
68*parse_end_state(char *statement, char *uname, int chars_remaining);
69static int process_statement(build_image_context *context, char *statement);
70
71static parse_item s_top_level_items[] =
72{
73 { "Bctfile=", token_bct_file, parse_bct_file },
74 { "Attribute=", token_attribute, parse_value_u32 },
75 { "Attribute[", token_attribute, parse_array },
76 { "BootLoader=", token_bootloader, parse_bootloader },
77 { "Redundancy=", token_redundancy, parse_value_u32 },
78 { "Version=", token_version, parse_value_u32 },
79 { "AddOn[", token_addon, parse_addon },
80 { NULL, 0, NULL } /* Must be last */
81};
82
83/* Macro to simplify parser code a bit. */
84#define PARSE_COMMA(x) if (*rest != ',') return (x); rest++
85
86/* This parsing code was initially borrowed from nvcamera_config_parse.c. */
87/* Returns the address of the character after the parsed data. */
88static char *
89parse_u32(char *statement, u_int32_t *val)
90{
91 u_int32_t value = 0;
92
93 while (*statement=='0') {
94 statement++;
95 }
96
97 if (*statement=='x' || *statement=='X') {
98 statement++;
99 while (((*statement >= '0') && (*statement <= '9')) ||
100 ((*statement >= 'a') && (*statement <= 'f')) ||
101 ((*statement >= 'A') && (*statement <= 'F'))) {
102 value *= 16;
103 if ((*statement >= '0') && (*statement <= '9')) {
104 value += (*statement - '0');
105 } else if ((*statement >= 'A') &&
106 (*statement <= 'F')) {
107 value += ((*statement - 'A')+10);
108 } else {
109 value += ((*statement - 'a')+10);
110 }
111 statement++;
112 }
113 } else {
114 while (*statement >= '0' && *statement <= '9') {
115 value = value*10 + (*statement - '0');
116 statement++;
117 }
118 }
119 *val = value;
120 return statement;
121}
122
123/* This parsing code was initially borrowed from nvcamera_config_parse.c. */
124/* Returns the address of the character after the parsed data. */
125static char *
126parse_filename(char *statement, char *name, int chars_remaining)
127{
128 while (((*statement >= '0') && (*statement <= '9')) ||
129 ((*statement >= 'a') && (*statement <= 'z')) ||
130 ((*statement >= 'A') && (*statement <= 'Z')) ||
131 (*statement == '\\') ||
132 (*statement == '/' ) ||
133 (*statement == '~' ) ||
134 (*statement == '_' ) ||
135 (*statement == '-' ) ||
136 (*statement == '+' ) ||
137 (*statement == ':' ) ||
138 (*statement == '.' )) {
139 /* Check if the filename buffer is out of space, preserving one
140 * character to null terminate the string.
141 */
142 chars_remaining--;
143
144 if (chars_remaining < 1)
145 return NULL;
146 *name++ = *statement++;
147 }
148
149 /* Null terminate the filename. */
150 *name = '\0';
151
152 return statement;
153}
154
155/*
156 * parse_bootloader(): Processes commands to set a bootloader.
157 */
158static int parse_bootloader(build_image_context *context,
159 parse_token token,
160 char *rest)
161{
162 char filename[MAX_BUFFER];
163 char e_state[MAX_STR_LEN];
164 u_int32_t load_addr;
165 u_int32_t entry_point;
166
167 assert(context != NULL);
168 assert(rest != NULL);
169
170 /* Parse the file name. */
171 rest = parse_filename(rest, filename, MAX_BUFFER);
172 if (rest == NULL)
173 return 1;
174
175 PARSE_COMMA(1);
176
177 /* Parse the load address. */
178 rest = parse_u32(rest, &load_addr);
179 if (rest == NULL)
180 return 1;
181
182 PARSE_COMMA(1);
183
184 /* Parse the entry point. */
185 rest = parse_u32(rest, &entry_point);
186 if (rest == NULL)
187 return 1;
188
189 PARSE_COMMA(1);
190
191 /* Parse the end state. */
192 rest = parse_end_state(rest, e_state, MAX_STR_LEN);
193 if (rest == NULL)
194 return 1;
195 if (strncmp(e_state, "Complete", strlen("Complete")))
196 return 1;
197
198 /* Parsing has finished - set the bootloader */
199 return set_bootloader(context, filename, load_addr, entry_point);
200}
201
202/*
203 * parse_array(): Processes commands to set an array value.
204 */
205static int
206parse_array(build_image_context *context, parse_token token, char *rest)
207{
208 u_int32_t index;
209 u_int32_t value;
210
211 assert(context != NULL);
212 assert(rest != NULL);
213
214 /* Parse the index. */
215 rest = parse_u32(rest, &index);
216 if (rest == NULL)
217 return 1;
218
219 /* Parse the closing bracket. */
220 if (*rest != ']')
221 return 1;
222 rest++;
223
224 /* Parse the equals sign.*/
225 if (*rest != '=')
226 return 1;
227 rest++;
228
229 /* Parse the value based on the field table. */
230 switch(token) {
231 case token_attribute:
232 rest = parse_u32(rest, &value);
233 break;
234
235 default:
236 /* Unknown token */
237 return 1;
238 }
239
240 if (rest == NULL)
241 return 1;
242
243 /* Store the result. */
244 return context_set_array(context, index, token, value);
245}
246
247/*
248 * parse_value_u32(): General handler for setting u_int32_t values in config files.
249 */
250static int parse_value_u32(build_image_context *context,
251 parse_token token,
252 char *rest)
253{
254 u_int32_t value;
255
256 assert(context != NULL);
257 assert(rest != NULL);
258
259 rest = parse_u32(rest, &value);
260 if (rest == NULL)
261 return 1;
262
263 return context_set_value(context, token, value);
264}
265
266static int
267parse_bct_file(build_image_context *context, parse_token token, char *rest)
268{
269 char filename[MAX_BUFFER];
270
271 assert(context != NULL);
272 assert(rest != NULL);
273
274 /* Parse the file name. */
275 rest = parse_filename(rest, filename, MAX_BUFFER);
276 if (rest == NULL)
277 return 1;
278
279 /* Parsing has finished - set the bctfile */
280 context->bct_filename = filename;
281 /* Read the bct file to buffer */
282 read_bct_file(context);
283 return 0;
284}
285
286static char *
287parse_string(char *statement, char *uname, int chars_remaining)
288{
289 memset(uname, 0, chars_remaining);
290 while (((*statement >= '0') && (*statement <= '9')) ||
291 ((*statement >= 'A') && (*statement <= 'Z')) ||
292 ((*statement >= 'a') && (*statement <= 'z'))) {
293
294 *uname++ = *statement++;
295 if (--chars_remaining < 0) {
296 printf("String length beyond the boundary!!!");
297 return NULL;
298 }
299 }
300 *uname = '\0';
301 return statement;
302}
303
304static char *
305parse_end_state(char *statement, char *uname, int chars_remaining)
306{
307 while (((*statement >= 'a') && (*statement <= 'z')) ||
308 ((*statement >= 'A') && (*statement <= 'Z'))) {
309
310 *uname++ = *statement++;
311 if (--chars_remaining < 0)
312 return NULL;
313 }
314 *uname = '\0';
315 return statement;
316}
317
318
319/* Parse the addon component */
320static int
321parse_addon(build_image_context *context, parse_token token, char *rest)
322{
323 char filename[MAX_BUFFER];
324 char u_name[4];
325 char e_state[MAX_STR_LEN];
326 u_int32_t index;
327 u_int32_t item_attr;
328 u_int32_t others;
329 char other_str[MAX_STR_LEN];
330
331 assert(context != NULL);
332 assert(rest != NULL);
333
334 /* Parse the index. */
335 rest = parse_u32(rest, &index);
336 if (rest == NULL)
337 return 1;
338
339 /* Parse the closing bracket. */
340 if (*rest != ']')
341 return 1;
342 rest++;
343
344 /* Parse the equals sign.*/
345 if (*rest != '=')
346 return 1;
347 rest++;
348
349 rest = parse_filename(rest, filename, MAX_BUFFER);
350 if (rest == NULL)
351 return 1;
352 if (set_addon_filename(context, filename, index) != 0)
353 return 1;
354
355 PARSE_COMMA(1);
356
357 rest = parse_string(rest, u_name, 3);
358 if (rest == NULL) {
359 printf("Unique name should be 3 characters.\n");
360 return 1;
361 }
362 if (set_unique_name(context, u_name, index) != 0)
363 return 1;
364
365 PARSE_COMMA(1);
366
367 rest = parse_u32(rest, &item_attr);
368 if (rest == NULL)
369 return 1;
370 if (set_addon_attr(context, item_attr, index) != 0)
371 return 1;
372
373 PARSE_COMMA(1);
374
375 if (*rest == '0' && (*(rest + 1) == 'x' ||*(rest + 1) == 'X')) {
376 rest = parse_u32(rest, &others);
377 if (set_other_field(context, NULL, others, index) != 0)
378 return 1;
379 } else {
380 rest = parse_string(rest, other_str, 16);
381 if (set_other_field(context, other_str, 0, index) != 0)
382 return 1;
383 }
384 if (rest == NULL)
385 return 1;
386
387 PARSE_COMMA(1);
388
389 rest = parse_end_state(rest, e_state, MAX_STR_LEN);
390 if (rest == NULL)
391 return 1;
392 if (strncmp(e_state, "Complete", strlen("Complete")))
393 return 1;
394 return 0;
395}
396
397/* Return 0 on success, 1 on error */
398static int
399process_statement(build_image_context *context, char *statement)
400{
401 int i;
402 char *rest;
403
404 for (i = 0; s_top_level_items[i].prefix != NULL; i++) {
405 if (!strncmp(s_top_level_items[i].prefix, statement,
406 strlen(s_top_level_items[i].prefix))) {
407 rest = statement + strlen(s_top_level_items[i].prefix);
408
409 return s_top_level_items[i].process(context,
410 s_top_level_items[i].token,
411 rest);
412 }
413 }
414
415 /* If this point was reached, there was a processing error. */
416 return 1;
417}
418
419/* Note: Basic parsing borrowed from nvcamera_config.c */
420void process_config_file(build_image_context *context)
421{
422 char buffer[MAX_BUFFER];
423 int space = 0;
424 char current;
425 u_int8_t c_eol_comment_start = 0; // True after first slash
426 u_int8_t comment = 0;
427 u_int8_t string = 0;
428 u_int8_t equal_encounter = 0;
429
430 assert(context != NULL);
431 assert(context->config_file != NULL);
432
433 while ((current = fgetc(context->config_file)) !=EOF) {
434 if (space >= (MAX_BUFFER-1)) {
435 /* if we exceeded the max buffer size, it is likely
436 due to a missing semi-colon at the end of a line */
437 printf("Config file parsing error!");
438 exit(1);
439 }
440
441 /* Handle failure to complete "//" comment token.
442 Insert the '/' into the busffer and proceed with
443 processing the current character. */
444 if (c_eol_comment_start && current != '/') {
445 c_eol_comment_start = 0;
446 buffer[space++] = '/';
447 }
448
449 switch (current) {
450 case '\"': /* " indicates start or end of a string */
451 if (!comment) {
452 string ^= 1;
453 buffer[space++] = current;
454 }
455 break;
456 case ';':
457 if (!string && !comment) {
458 buffer[space++] = '\0';
459
460 /* Process a statement. */
461 if (process_statement(context, buffer)) {
462 goto error;
463 }
464 space = 0;
465 equal_encounter = 0;
466 } else if (string) {
467 buffer[space++] = current;
468 }
469 break;
470
471 case '/':
472 if (!string && !comment) {
473 if (c_eol_comment_start) {
474 /* EOL comment started. */
475 comment = 1;
476 c_eol_comment_start = 0;
477 } else {
478 /* Potential start of eol comment. */
479 c_eol_comment_start = 1;
480 }
481 } else if (!comment) {
482 buffer[space++] = current;
483 }
484 break;
485
486 /* ignore whitespace. uses fallthrough */
487 case '\n':
488 case '\r': /* carriage returns end comments */
489 string = 0;
490 comment = 0;
491 c_eol_comment_start = 0;
492 case ' ':
493 case '\t':
494 if (string) {
495 buffer[space++] = current;
496 }
497 break;
498
499 case '#':
500 if (!string) {
501 comment = 1;
502 } else {
503 buffer[space++] = current;
504 }
505 break;
506
507 default:
508 if (!comment) {
509 buffer[space++] = current;
510 if (current == '=') {
511 if (!equal_encounter) {
512 equal_encounter = 1;
513 } else {
514 goto error;
515 }
516 }
517 }
518 break;
519 }
520 }
521
522 return;
523
524 error:
525 printf("Error parsing: %s\n", buffer);
526 exit(1);
527}