summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extra/usb_updater/.gitignore1
-rw-r--r--extra/usb_updater/Makefile7
-rw-r--r--extra/usb_updater/desc_parser.c377
-rw-r--r--extra/usb_updater/desc_parser.h58
-rw-r--r--extra/usb_updater/sample_descriptor87
5 files changed, 528 insertions, 2 deletions
diff --git a/extra/usb_updater/.gitignore b/extra/usb_updater/.gitignore
index 870b0817e5..2e16c2ff2a 100644
--- a/extra/usb_updater/.gitignore
+++ b/extra/usb_updater/.gitignore
@@ -2,3 +2,4 @@ gsctool
usb_updater2
*.d
*.o
+dp \ No newline at end of file
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile
index fedb959356..86632903c8 100644
--- a/extra/usb_updater/Makefile
+++ b/extra/usb_updater/Makefile
@@ -41,7 +41,7 @@ LIBS_common = -lfmap
all: $(PROGRAMS)
-GSCTOOL_SOURCES := gsctool.c
+GSCTOOL_SOURCES := gsctool.c desc_parser.c
GSCTOOL_OBJS := $(patsubst %.c,%.o,$(GSCTOOL_SOURCES))
DEPS := $(patsubst %.c,%.d,$(GSCTOOL_SOURCES))
@@ -59,6 +59,9 @@ usb_updater2: usb_updater2.c Makefile
.PHONY: clean
clean:
- rm -rf $(PROGRAMS) *~ *.o *.d
+ rm -rf $(PROGRAMS) *~ *.o *.d dp
+
+parser_debug: desc_parser.c
+ gcc -g -O0 -DTEST_PARSER desc_parser.c -o dp
-include $(DEPS)
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
diff --git a/extra/usb_updater/desc_parser.h b/extra/usb_updater/desc_parser.h
new file mode 100644
index 0000000000..faa80d1a63
--- /dev/null
+++ b/extra/usb_updater/desc_parser.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+#ifndef __EXTRA_USB_UPDATER_DESC_PARSER_H
+#define __EXTRA_USB_UPDATER_DESC_PARSER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct result_node {
+ uint8_t expected_result[32];
+};
+
+enum range_type_t {
+ NOT_A_RANGE,
+ AP_RANGE,
+ EC_RANGE,
+ EC_GANG_RANGE,
+};
+
+struct addr_range {
+ enum range_type_t range_type;
+ uint32_t base_addr;
+ uint32_t range_size;
+ size_t variant_count; /* Set to zero for dump ranges. */
+ struct result_node variants[0];
+};
+
+/* Board description retrieval API includes the following functions. */
+
+/*
+ * In the given hash database file find board by its ID. Return zero on
+ * success, or OS error of error. In particular ENODATA is returned if the
+ * section for the required board ID is not found in the file.
+ */
+int parser_find_board(const char *hash_file_name, const char board_id[4]);
+
+/*
+ * Find next range for the previousely defined board, parse it into the
+ * addr_range structure and return pointer to the parsed structure to the
+ * caller, set pointer to NULL if no more entries are available or in case of
+ * error.
+ *
+ * Caller of this function is responsible for returning memory allocated for
+ * the entry.
+ *
+ * Return value set to zero on success, or to OS error if one occurs. EIO is
+ * used if an attmept to get next range is made before hash database file was
+ * opened and board entry in it was found.
+ */
+int parser_get_next_range(struct addr_range **range);
+
+/* Close the hash database file. */
+void parser_done(void);
+
+#endif // __EXTRA_USB_UPDATER_DESC_PARSER_H
diff --git a/extra/usb_updater/sample_descriptor b/extra/usb_updater/sample_descriptor
new file mode 100644
index 0000000000..1566e9e2e1
--- /dev/null
+++ b/extra/usb_updater/sample_descriptor
@@ -0,0 +1,87 @@
+# 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.
+#
+# Hash descriptor database file consists of sections for various Chrome OS
+# boards. Each board description section starts with a line of 4 characters
+# which is the board ID (the same as the board's RLZ code).
+#
+# Each board description section includes variable number of range
+# descriptor entries, each entry consisting of semicolon separated fields:
+#
+# {a|e|g}:{h|d}:base_addr:size[:value[:value[:value...]]]]
+#
+# Where
+#
+# - the first sindgle character field defines the way the range is accessed:
+# a - AP flash
+# e - EC flash
+# g - EC flash requiring gang programming mode
+# - the second single character field defines the range type
+# h - Cr50 returns the hash of the range
+# d - Cr50 returns actual contents of the range (hex dump)
+# - the third and and forth fields are base address and size of the range
+# - ranges of type 'h' include one or more values for the hash of the range.
+#
+# Descriptor entries can be split along multiple lines. Each entry is
+# terminated by an empty line. Board description section is completed when
+# another board ID or end of file is encountered.
+#
+# All values are expressed in hex. Repeating empty lines and lines starting
+# with '#' are ignored.
+#
+
+QZUX
+
+# 1: Valid hash section.
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503:
+446c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
+
+# 2: Valid dump section.
+a:d:10:10
+
+# 3: Valid hash section.
+e:h:0:100:
+55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
+444442badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87:
+443322badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
+
+# 4: Invalid dump section (includes hash)
+a:d:20:10:55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87
+
+# 5: Invalid hash section (does not include hash)
+e:h:0:100:
+
+# 6: Another invalid hash section (does not include hash)
+e:h:0:100:
+
+# extra empty lines
+
+
+# 7: Invalid hash section (hash too short)
+e:h:0:100:
+55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb8
+
+# 8: Invalid hash section (hash too long)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf35034:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503
+
+# 9: Invalid hash section (hash includes non-hex value)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd7xe0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
+
+# 10: Invalid hash section (hash does not include 3 variants)
+a:h:0:10000:
+756c41b90ac9aa23a6c98ce13549dccd75e0a83f8537eb834d9cfc3d12bf3503:
+336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505
+
+# 11: Invalid dump section (size includes non hex character)
+a:d:10:10x
+
+ABCD
+
+a:d:10:10