nvidia-cbootimage/parse.c

809 lines
19 KiB
C

/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See file CREDITS for list of people who contributed to this
* project.
*/
/*
* parse.c - Parsing support for the cbootimage tool
*/
#include <ctype.h>
#include "parse.h"
#include "cbootimage.h"
#include "data_layout.h"
#include "crypto.h"
#include "set.h"
/*
* Function prototypes
*
* ParseXXX() parses XXX in the input
* SetXXX() sets state based on the parsing results but does not perform
* any parsing of its own
* A ParseXXX() function may call other parse functions and set functions.
* A SetXXX() function may not call any parseing functions.
*/
static int
set_array(build_image_context *context,
u_int32_t index,
parse_token token,
u_int32_t value);
static char *parse_u32(char *str, u_int32_t *val);
static char *parse_u8(char *str, u_int32_t *val);
static char *parse_filename(char *str, char *name, int chars_remaining);
static char *parse_enum(build_image_context *context,
char *str,
enum_item *table,
u_int32_t *val);
static char
*parse_field_name(char *rest, field_item *field_table, field_item **field);
static char
*parse_field_value(build_image_context *context,
char *rest,
field_item *field,
u_int32_t *value);
static int
parse_array(build_image_context *context, parse_token token, char *rest);
static int
parse_bootloader(build_image_context *context, parse_token token, char *rest);
static int
parse_value_u32(build_image_context *context, parse_token token, char *rest);
static int
parse_bct_file(build_image_context *context, parse_token token, char *rest);
static char
*parse_end_state(char *str, char *uname, int chars_remaining);
static int
parse_dev_param(build_image_context *context, parse_token token, char *rest);
static int
parse_sdram_param(build_image_context *context, parse_token token, char *rest);
static int process_statement(build_image_context *context,
char *str,
u_int8_t simple_parse);
static parse_item parse_simple_items[] =
{
{ "Bctfile=", token_bct_file, parse_bct_file },
{ "BootLoader=", token_bootloader, parse_bootloader },
{ "Redundancy=", token_redundancy, parse_value_u32 },
{ "Bctcopy=", token_bct_copy, parse_value_u32 },
{ "Version=", token_version, parse_value_u32 },
{ "PreBctPadBlocks=", token_pre_bct_pad_blocks, parse_value_u32 },
{ NULL, 0, NULL } /* Must be last */
};
static parse_item s_top_level_items[] = {
{ "Bctfile=", token_bct_file, parse_bct_file },
{ "Attribute=", token_attribute, parse_value_u32 },
{ "Attribute[", token_attribute, parse_array },
{ "PageSize=", token_page_size, parse_value_u32 },
{ "BlockSize=", token_block_size, parse_value_u32 },
{ "PartitionSize=", token_partition_size, parse_value_u32 },
{ "DevType[", token_dev_type, parse_array },
{ "DeviceParam[", token_dev_param, parse_dev_param },
{ "SDRAM[", token_sdram, parse_sdram_param },
{ "BootLoader=", token_bootloader, parse_bootloader },
{ "Redundancy=", token_redundancy, parse_value_u32 },
{ "Bctcopy=", token_bct_copy, parse_value_u32 },
{ "Version=", token_version, parse_value_u32 },
{ "OdmData=", token_odm_data, parse_value_u32 },
{ NULL, 0, NULL } /* Must be last */
};
/* Macro to simplify parser code a bit. */
#define PARSE_COMMA(x) if (*rest != ',') return (x); rest++
/*
* Parse the given string and find the u32 dec/hex number.
*
* @param str String to parse
* @param val Returns value that was parsed
* @return the remainder of the string after the number was parsed
*/
static char *
parse_u32(char *str, u_int32_t *val)
{
u_int32_t value = 0;
u_int32_t digit;
while (*str == '0')
str++;
if (tolower(*str) == 'x') {
str++;
while (isxdigit(*str)) {
value *= 16;
digit = tolower(*str);
value += digit <= '9' ? digit - '0' : digit - 'a' + 10;
str++;
}
} else {
while (*str >= '0' && *str <= '9') {
value = value*10 + (*str - '0');
str++;
}
}
*val = value;
return str;
}
/*
* Parse the given string and find the u8 dec/hex number.
*
* @param str String to parse
* @param val Returns value that was parsed
* @return the remainder of the string after the number was parsed
*/
static char *
parse_u8(char *str, u_int32_t *val)
{
char *retval;
retval = parse_u32(str, val);
if (*val > 0xff) {
printf("Warning: Parsed 8-bit value that exceeded 8-bits.\n");
printf(" Parsed value = %d. Remaining text = %s\n",
*val, retval);
}
return retval;
}
/*
* Parse the given string and find the file name then
* return the rest of the string.
*
* @param str String to parse
* @param name Returns the filename that was parsed
* @param chars_remaining The maximum length of filename
* @return the remainder of the string after the name was parsed
*/
static char *
parse_filename(char *str, char *name, int chars_remaining)
{
/*
* Check if the filename buffer is out of space, preserving one
* character to null terminate the string.
*/
while (isalnum(*str) || strchr("\\/~_-+:.", *str)) {
chars_remaining--;
if (chars_remaining < 1)
return NULL;
*name++ = *str++;
}
/* Null terminate the filename. */
*name = '\0';
return str;
}
/*
* Parse the given string and find the match field name listed
* in field table.
*
* @param rest String to parse
* @param field_table The field table to parse
* @param field Returns the field item that was parsed
* @return NULL or the remainder of the string after the field item was parsed
*/
static char
*parse_field_name(char *rest, field_item *field_table, field_item **field)
{
u_int32_t i;
u_int32_t field_name_len = 0;
assert(field_table != NULL);
assert(rest != NULL);
assert(field != NULL);
while (rest[field_name_len] != '=')
field_name_len++;
/* Parse the field name. */
for (i = 0; field_table[i].name != NULL; i++) {
if ((strlen(field_table[i].name) == field_name_len) &&
!strncmp(field_table[i].name,
rest,
field_name_len)) {
*field = &(field_table[i]);
rest = rest + field_name_len;
return rest;
}
}
/* Field wasn't found or a parse error occurred. */
return NULL;
}
/*
* Parse the value based on the field table
*
* @param context The main context pointer
* @param rest String to parse
* @param field Field item to parse
* @param value Returns the value that was parsed
* @return the remainder of the string after the value was parsed
*/
static char
*parse_field_value(build_image_context *context,
char *rest,
field_item *field,
u_int32_t *value)
{
assert(rest != NULL);
assert(field != NULL);
assert((field->type != field_type_enum)
|| (field->enum_table != NULL));
switch (field->type) {
case field_type_enum:
rest = parse_enum(context, rest, field->enum_table, value);
break;
case field_type_u32:
rest = parse_u32(rest, value);
break;
case field_type_u8:
rest = parse_u8(rest, value);
break;
default:
printf("Unexpected field type %d at line %d\n",
field->type, __LINE__);
rest = NULL;
break;
}
return rest;
}
/*
* Parse the given string and find the match enum item listed
* in table.
*
* @param context The main context pointer
* @param str String to parse
* @param table Enum item table to parse
* @param value Returns the value that was parsed
* @return the remainder of the string after the item was parsed
*/
static char *
parse_enum(build_image_context *context,
char *str,
enum_item *table,
u_int32_t *val)
{
int i;
char *rest;
for (i = 0; table[i].name != NULL; i++) {
if (!strncmp(table[i].name, str,
strlen(table[i].name))) {
*val = table[i].value;
rest = str + strlen(table[i].name);
return rest;
}
}
return parse_u32(str, val);
}
/*
* Parse the given string and find the bootloader file name, load address and
* entry point information then call set_bootloader function.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int parse_bootloader(build_image_context *context,
parse_token token,
char *rest)
{
char filename[MAX_BUFFER];
char e_state[MAX_STR_LEN];
u_int32_t load_addr;
u_int32_t entry_point;
assert(context != NULL);
assert(rest != NULL);
if (context->generate_bct != 0)
return 0;
/* Parse the file name. */
rest = parse_filename(rest, filename, MAX_BUFFER);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the load address. */
rest = parse_u32(rest, &load_addr);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the entry point. */
rest = parse_u32(rest, &entry_point);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the end state. */
rest = parse_end_state(rest, e_state, MAX_STR_LEN);
if (rest == NULL)
return 1;
if (strncmp(e_state, "Complete", strlen("Complete")))
return 1;
/* Parsing has finished - set the bootloader */
return set_bootloader(context, filename, load_addr, entry_point);
}
/*
* Parse the given string and find the array items in config file.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int
parse_array(build_image_context *context, parse_token token, char *rest)
{
u_int32_t index;
u_int32_t value;
assert(context != NULL);
assert(rest != NULL);
/* Parse the index. */
rest = parse_u32(rest, &index);
if (rest == NULL)
return 1;
/* Parse the closing bracket. */
if (*rest != ']')
return 1;
rest++;
/* Parse the equals sign.*/
if (*rest != '=')
return 1;
rest++;
/* Parse the value based on the field table. */
switch (token) {
case token_attribute:
rest = parse_u32(rest, &value);
break;
case token_dev_type:
rest = parse_enum(context,
rest,
s_devtype_table_t20,
&value);
break;
default:
/* Unknown token */
return 1;
}
if (rest == NULL)
return 1;
/* Store the result. */
return set_array(context, index, token, value);
}
/*
* Call hw interface to set the value for array item in bct such as device
* type and bootloader attribute.
*
* @param context The main context pointer
* @param index The index for array
* @param token The parse token value
* @param value The value to set
* @return 0 and -ENODATA for success and failure
*/
static int
set_array(build_image_context *context,
u_int32_t index,
parse_token token,
u_int32_t value)
{
int err = 0;
assert(context != NULL);
switch (token) {
case token_attribute:
err = g_bct_parse_interf->setbl_param(index,
token_bl_attribute,
&value,
context->bct);
break;
case token_dev_type:
err = g_bct_parse_interf->set_dev_param(context,
index,
token_dev_type,
value);
break;
default:
break;
}
return err;
}
/*
* General handler for setting u_int32_t values in config files.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int parse_value_u32(build_image_context *context,
parse_token token,
char *rest)
{
u_int32_t value;
assert(context != NULL);
assert(rest != NULL);
rest = parse_u32(rest, &value);
if (rest == NULL)
return 1;
return context_set_value(context, token, value);
}
/*
* Parse the given string and find the bct file name.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int
parse_bct_file(build_image_context *context, parse_token token, char *rest)
{
char filename[MAX_BUFFER];
assert(context != NULL);
assert(rest != NULL);
/* Parse the file name. */
rest = parse_filename(rest, filename, MAX_BUFFER);
if (rest == NULL)
return 1;
/* Parsing has finished - set the bctfile */
context->bct_filename = filename;
/* Read the bct file to buffer */
read_bct_file(context);
update_context(context);
return 0;
}
static char *
parse_end_state(char *str, char *uname, int chars_remaining)
{
while (isalpha(*str)) {
*uname++ = *str++;
if (--chars_remaining < 0)
return NULL;
}
*uname = '\0';
return str;
}
/*
* Parse the given string and find device parameter listed in device table
* and value for this device parameter. If match, call the corresponding
* function in the table to set device parameter.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int
parse_dev_param(build_image_context *context, parse_token token, char *rest)
{
u_int32_t i;
u_int32_t value;
field_item *field;
u_int32_t index;
parse_subfield_item *device_type_table;
parse_subfield_item *device_item = NULL;
assert(context != NULL);
assert(rest != NULL);
if (context->boot_data_version == NVBOOT_BOOTDATA_VERSION(3, 1))
device_type_table = s_device_type_table_t30;
else
device_type_table = s_device_type_table_t20;
/* Parse the index. */
rest = parse_u32(rest, &index);
if (rest == NULL)
return 1;
/* Parse the closing bracket. */
if (*rest != ']')
return 1;
rest++;
/* Parse the following '.' */
if (*rest != '.')
return 1;
rest++;
/* Parse the device name. */
for (i = 0; device_type_table[i].prefix != NULL; i++) {
if (!strncmp(device_type_table[i].prefix,
rest, strlen(device_type_table[i].prefix))) {
device_item = &(device_type_table[i]);
rest = rest + strlen(device_type_table[i].prefix);
/* Parse the field name. */
rest = parse_field_name(rest,
device_type_table[i].field_table,
&field);
if (rest == NULL)
return 1;
/* Parse the equals sign.*/
if (*rest != '=')
return 1;
rest++;
/* Parse the value based on the field table. */
rest = parse_field_value(context, rest, field, &value);
if (rest == NULL)
return 1;
return device_item->process(context,
index, field->token, value);
}
}
return 1;
}
/*
* Parse the given string and find sdram parameter and value in config
* file. If match, call the corresponding function set the sdram parameter.
*
* @param context The main context pointer
* @param token The parse token value
* @param rest String to parse
* @return 0 and 1 for success and failure
*/
static int
parse_sdram_param(build_image_context *context, parse_token token, char *rest)
{
u_int32_t value;
field_item *field;
u_int32_t index;
assert(context != NULL);
assert(rest != NULL);
/* Parse the index. */
rest = parse_u32(rest, &index);
if (rest == NULL)
return 1;
/* Parse the closing bracket. */
if (*rest != ']')
return 1;
rest++;
/* Parse the following '.' */
if (*rest != '.')
return 1;
rest++;
/* Parse the field name. */
if (context->boot_data_version == NVBOOT_BOOTDATA_VERSION(3, 1))
rest = parse_field_name(rest, s_sdram_field_table_t30, &field);
else
rest = parse_field_name(rest, s_sdram_field_table_t20, &field);
if (rest == NULL)
return 1;
/* Parse the equals sign.*/
if (*rest != '=')
return 1;
rest++;
/* Parse the value based on the field table. */
rest = parse_field_value(context, rest, field, &value);
if (rest == NULL)
return 1;
/* Store the result. */
return g_bct_parse_interf->set_sdram_param(context,
index,
field->token,
value);
}
/*
* Compare the given string with item listed in table.
* Execute the proper process function if match.
*
* @param context The main context pointer
* @param str String to parse
* @param simple_parse Simple parse flag
* @return 0 and 1 for success and failure
*/
static int
process_statement(build_image_context *context,
char *str,
u_int8_t simple_parse)
{
int i;
char *rest;
parse_item *cfg_parse_item;
if (simple_parse == 0)
cfg_parse_item = s_top_level_items;
else
cfg_parse_item = parse_simple_items;
for (i = 0; cfg_parse_item[i].prefix != NULL; i++) {
if (!strncmp(cfg_parse_item[i].prefix, str,
strlen(cfg_parse_item[i].prefix))) {
rest = str + strlen(cfg_parse_item[i].prefix);
return cfg_parse_item[i].process(context,
cfg_parse_item[i].token,
rest);
}
}
/* If this point was reached, there was a processing error. */
return 1;
}
/*
* The main function parse the config file.
*
* @param context The main context pointer
* @param simple_parse Simple parse flag
*/
void process_config_file(build_image_context *context, u_int8_t simple_parse)
{
char buffer[MAX_BUFFER];
int space = 0;
char current;
u_int8_t c_eol_comment_start = 0; /* True after first slash */
u_int8_t comment = 0;
u_int8_t string = 0;
u_int8_t equal_encounter = 0;
assert(context != NULL);
assert(context->config_file != NULL);
while ((current = fgetc(context->config_file)) != EOF) {
if (space >= (MAX_BUFFER-1)) {
/* if we exceeded the max buffer size, it is likely
due to a missing semi-colon at the end of a line */
printf("Config file parsing error!");
exit(1);
}
/* Handle failure to complete "//" comment token.
Insert the '/' into the busffer and proceed with
processing the current character. */
if (c_eol_comment_start && current != '/') {
c_eol_comment_start = 0;
buffer[space++] = '/';
}
switch (current) {
case '\"': /* " indicates start or end of a string */
if (!comment) {
string ^= 1;
buffer[space++] = current;
}
break;
case ';':
if (!string && !comment) {
buffer[space++] = '\0';
if (process_statement(context,
buffer,
simple_parse))
goto error;
space = 0;
equal_encounter = 0;
} else if (string)
buffer[space++] = current;
break;
case '/':
if (!string && !comment) {
if (c_eol_comment_start) {
/* EOL comment started. */
comment = 1;
c_eol_comment_start = 0;
} else {
/* Potential start of eol comment. */
c_eol_comment_start = 1;
}
} else if (!comment)
buffer[space++] = current;
break;
/* ignore whitespace. uses fallthrough */
case '\n':
case '\r': /* carriage returns end comments */
string = 0;
comment = 0;
c_eol_comment_start = 0;
case ' ':
case '\t':
if (string)
buffer[space++] = current;
break;
case '#':
if (!string)
comment = 1;
else
buffer[space++] = current;
break;
default:
if (!comment) {
buffer[space++] = current;
if (current == '=') {
if (!equal_encounter)
equal_encounter = 1;
else
goto error;
}
}
break;
}
}
return;
error:
printf("Error parsing: %s\n", buffer);
exit(1);
}