summaryrefslogtreecommitdiff
path: root/extra/usb_updater/desc_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'extra/usb_updater/desc_parser.c')
-rw-r--r--extra/usb_updater/desc_parser.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/extra/usb_updater/desc_parser.c b/extra/usb_updater/desc_parser.c
new file mode 100644
index 0000000000..04f144457c
--- /dev/null
+++ b/extra/usb_updater/desc_parser.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "desc_parser.h"
+
+static FILE *hash_file_;
+static int line_count_;
+static int section_count_;
+
+/* Size of the retrieved string or negative OS error value. */
+static ssize_t get_next_line(char *next_line, size_t line_size)
+{
+ size_t index = 0;
+
+ while (fgets(next_line + index, line_size - index, hash_file_)) {
+ line_count_++;
+
+ if (next_line[index] == '#')
+ continue; /* Skip the comment */
+
+ if (next_line[index] == '\n') {
+ /*
+ * This is an empty line, return all collected data,
+ * pontintially an array of size zero if this is a
+ * repeated empty line.
+ */
+ next_line[index] = '\0';
+ return index;
+ }
+
+ /* Make sure next string overwrites this string's newline. */
+ index += strlen(next_line + index) - 1;
+
+ if (index >= (line_size - 1)) {
+ fprintf(stderr, "%s: Input overflow in line %d\n",
+ __func__, line_count_);
+ return -EOVERFLOW;
+ }
+ }
+
+ if (index) {
+ /*
+ * This must be the last line in the file with no empty line
+ * after it. Drop the closing newline, if it is there.
+ */
+ if (next_line[index] == '\n')
+ next_line[index--] = '\0';
+
+ return index;
+ }
+ return errno ? -errno : -ENODATA;
+}
+
+static int get_next_token(char *input, size_t expected_size, char **output)
+{
+ char *next_colon;
+
+ next_colon = strchr(input, ':');
+ if (next_colon)
+ *next_colon = '\0';
+ if (!next_colon || (expected_size &&
+ strlen(input) != expected_size)) {
+ fprintf(stderr, "Invalid entry in section %d\n",
+ section_count_);
+ return -EINVAL;
+ }
+
+ *output = next_colon + 1;
+ return 0;
+}
+
+static int get_hex_value(char *input, char **output)
+{
+ char *e;
+ long int value;
+
+ if (strchr(input, ':'))
+ get_next_token(input, 0, output);
+ else
+ *output = NULL;
+
+ value = strtol(input, &e, 16);
+ if (e && *e) {
+ fprintf(stderr, "Invalid hex value %s in section %d\n",
+ input, section_count_);
+ return -EINVAL;
+ }
+
+ return value;
+}
+
+static int parse_range(char *next_line,
+ size_t line_len,
+ struct addr_range *parsed_range)
+{
+ char *line_cursor;
+ char *next_token;
+ int is_a_hash_range;
+ struct result_node *node;
+ int value;
+
+ section_count_++;
+ line_cursor = next_line;
+
+ /* Range type. */
+ if (get_next_token(line_cursor, 1, &next_token))
+ return -EINVAL;
+
+ switch (*line_cursor) {
+ case 'a':
+ parsed_range->range_type = AP_RANGE;
+ break;
+ case 'e':
+ parsed_range->range_type = EC_RANGE;
+ break;
+ case 'g':
+ parsed_range->range_type = EC_GANG_RANGE;
+ break;
+ default:
+ fprintf(stderr, "Invalid range type %c in section %d\n",
+ *line_cursor, section_count_);
+ return -EINVAL;
+ }
+ line_cursor = next_token;
+
+ /* Hash or dump? */
+ if (get_next_token(line_cursor, 1, &next_token))
+ return -EINVAL;
+
+ switch (*line_cursor) {
+ case 'd':
+ is_a_hash_range = 0;
+ break;
+ case 'h':
+ is_a_hash_range = 1;
+ break;
+ default:
+ fprintf(stderr, "Invalid entry kind %c in section %d\n",
+ *line_cursor, section_count_);
+ return -EINVAL;
+ }
+ line_cursor = next_token;
+
+ /* Range base address. */
+ value = get_hex_value(line_cursor, &next_token);
+ if (value < 0)
+ return -EINVAL;
+ parsed_range->base_addr = value;
+
+ /* Range size. */
+ line_cursor = next_token;
+ value = get_hex_value(line_cursor, &next_token);
+ if (value < 0)
+ return -EINVAL;
+ parsed_range->range_size = value;
+
+ if (!next_token && is_a_hash_range) {
+ fprintf(stderr, "Missing hash in section %d\n", section_count_);
+ return -EINVAL;
+ }
+
+ if (next_token && !is_a_hash_range) {
+ fprintf(stderr, "Unexpected data in section %d\n",
+ section_count_);
+ return -EINVAL;
+ }
+
+ parsed_range->variant_count = 0;
+ if (!is_a_hash_range)
+ return 0; /* No more input for dump ranges. */
+
+ node = parsed_range->variants;
+ do { /* While line is not over. */
+ char c;
+ int i = 0;
+
+ line_cursor = next_token;
+ next_token = strchr(line_cursor, ':');
+ if (next_token)
+ *next_token++ = '\0';
+ if (strlen(line_cursor) != (2 * sizeof(*node))) {
+ fprintf(stderr,
+ "Invalid hash %zd size %zd in section %d\n",
+ parsed_range->variant_count + 1,
+ strlen(line_cursor), section_count_);
+ return -EINVAL;
+ }
+
+ while ((c = *line_cursor++) != 0) {
+ uint8_t nibble;
+
+ if (!isxdigit(c)) {
+ fprintf(stderr,
+ "Invalid hash %zd value in section %d\n",
+ parsed_range->variant_count + 1,
+ section_count_);
+ return -EINVAL;
+ }
+
+ if (c <= '9')
+ nibble = c - '0';
+ else if (c >= 'a')
+ nibble = c - 'a' + 10;
+ else
+ nibble = c - 'A' + 10;
+
+ if (i & 1)
+ node->expected_result[i / 2] |= nibble;
+ else
+ node->expected_result[i / 2] = nibble << 4;
+
+ i++;
+ }
+
+ node++;
+ parsed_range->variant_count++;
+
+ } while (next_token);
+
+ return 0;
+}
+
+int parser_get_next_range(struct addr_range **range)
+{
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ ssize_t entry_size;
+ struct addr_range *new_range;
+ int rv;
+
+ /*
+ * This is used to verify consistency of the description database,
+ * namely that all hash sections include the same numger of hash
+ * variants.
+ */
+ static size_t variant_count;
+
+ /*
+ * We come here after hash descriptor database file was opened and the
+ * current board's section has been found. Just in case check if the
+ * file has been opened.
+ */
+ if (!hash_file_ || !range)
+ return -EIO;
+
+ *range = NULL;
+ do {
+ entry_size = get_next_line(next_line, sizeof(next_line));
+ if (entry_size < 0)
+ return entry_size;
+ } while (!entry_size); /* Skip empty lines. */
+
+ if (entry_size == 4) /* Next board's entry must have been reached. */
+ return -ENODATA;
+
+ /* This sure will be enough to fit parsed structure contents. */
+ new_range = malloc(sizeof(*new_range) + entry_size);
+ if (!new_range) {
+ fprintf(stderr, "Failed to allocate %zd bytes\n",
+ sizeof(*new_range) + entry_size);
+ return -ENOMEM;
+ }
+
+ /* This must be a new descriptor section, lets parse it. */
+ rv = parse_range(next_line, entry_size, new_range);
+
+ if (rv) {
+ free(new_range);
+ return rv;
+ }
+
+ if (new_range->variant_count) {
+ /*
+ * A new range was found, if this is the first hash range we
+ * encountered, save its dimensions for future reference.
+ *
+ * If this is not the first one - verify that it has the same
+ * number of hash variants as all previous hash blocks.
+ */
+ if (!variant_count) {
+ variant_count = new_range->variant_count;
+ } else if (variant_count != new_range->variant_count) {
+ fprintf(stderr,
+ "Unexpected number of variants in section %d\n",
+ section_count_);
+ free(new_range);
+ return -EINVAL;
+ }
+ }
+
+ *range = new_range;
+ return 0;
+
+}
+
+int parser_find_board(const char *hash_file_name, const char *board_id)
+{
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ ssize_t id_len = strlen(board_id);
+
+ hash_file_ = fopen(hash_file_name, "r");
+ if (!hash_file_) {
+ fprintf(stderr, "Error:%s can not open file '%s'\n",
+ strerror(errno), hash_file_name);
+ return errno;
+ }
+
+ while (1) {
+ ssize_t entry_size;
+
+ entry_size = get_next_line(next_line, sizeof(next_line));
+ if (entry_size < 0) {
+ fclose(hash_file_);
+ return entry_size;
+ }
+
+ if ((entry_size == id_len) &&
+ !memcmp(next_line, board_id, id_len))
+ return 0;
+ }
+
+ fclose(hash_file_);
+ hash_file_ = NULL;
+ return errno;
+}
+
+void parser_done(void)
+{
+ if (!hash_file_)
+ return;
+
+ fclose(hash_file_);
+ hash_file_ = NULL;
+}
+
+#ifdef TEST_PARSER
+int main(int argc, char **argv)
+{
+ const char *board_name = "QZUX";
+ char next_line[1000]; /* Should be enough for the largest descriptor. */
+ int rv;
+ int count;
+
+ if (argc < 2) {
+ fprintf(stderr, "Name of the file to parse is required.\n");
+ return -1;
+ }
+
+ if (parser_find_board(argv[1], board_name)) {
+ fprintf(stderr, "Board %s NOT found\n", board_name);
+ return -1;
+ }
+
+ count = 0;
+ do {
+ struct addr_range *range;
+
+ rv = parser_get_next_range(&range);
+ count++;
+ printf("Section %d, rv %d\n", count, rv);
+ free(range); /* Freeing NULL is OK. */
+
+ } while (rv != -ENODATA);
+
+ return 0;
+}
+#endif