fmaptool: Introduce the fmd ("flashmap descriptor") language and compiler

This adds a compiler for a language whose textual representation of flashmap
regions will be used to describe the layout of flash chips that contain more
than just a single CBFS. Direct integration with cbfstool (via a new
command-line switch for the create action) is forthcoming but will be added
separately.

BUG=chromium:461875
TEST=Use Chromium OS's cros_bundle_firmware script on the fmap.dts file for
panther. Using the latter file as a reference, write a corresponding
fmap.fmd file and feed it through fmaptool. Run both binary output files
though the flashmap project's own flashmap_decode utility. Observe only
the expected differences.
BRANCH=None

Change-Id: I06b32d138dbef0a4e5ed43c81bd31c796fd5d669
Signed-off-by: Sol Boucher <solb@chromium.org>
Original-Commit-Id: 005ab67eb594e21489cf31036aedaea87e0c7142
Original-Change-Id: Ia08f28688efdbbfc70c255916b8eb7eb0eb07fb2
Original-Signed-off-by: Sol Boucher <solb@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/255031
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Original-Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
Reviewed-on: http://review.coreboot.org/9942
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/util/cbfstool/fmd_parser.y b/util/cbfstool/fmd_parser.y
new file mode 100644
index 0000000..292fba3
--- /dev/null
+++ b/util/cbfstool/fmd_parser.y
@@ -0,0 +1,190 @@
+/*
+ * fmd_parser.y, parser generator for flashmap descriptor language
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+%{
+#include "fmd_scanner.h"
+
+#include <stdlib.h>
+
+struct flashmap_descriptor *res = NULL;
+%}
+
+%union {
+	unsigned intval;
+	char *strval;
+	struct unsigned_option maybe_intval;
+	struct flashmap_descriptor *region_ptr;
+	struct descriptor_list region_listhdr;
+}
+
+%code requires {
+#include "fmd.h"
+#include "option.h"
+
+#include <stdbool.h>
+
+struct descriptor_node {
+	struct flashmap_descriptor *val;
+	struct descriptor_node *next;
+};
+
+struct descriptor_list {
+	size_t len;
+	struct descriptor_node *head;
+	struct descriptor_node *tail;
+};
+
+extern struct flashmap_descriptor *res;
+
+struct flashmap_descriptor *parse_descriptor(char *name,
+	struct unsigned_option offset, struct unsigned_option size,
+					struct descriptor_list children);
+void yyerror(const char *s);
+}
+
+%token <intval> INTEGER
+%token OCTAL
+%token <strval> STRING
+
+%type <region_ptr> flash_region
+%type <strval> region_name
+%type <strval> region_annotation_opt
+%type <strval> region_annotation
+%type <maybe_intval> region_offset_opt
+%type <maybe_intval> region_offset
+%type <maybe_intval> region_size_opt
+%type <maybe_intval> region_size
+%type <region_listhdr> region_list_opt
+%type <region_listhdr> region_list
+%type <region_listhdr> region_list_entries
+
+%%
+
+flash_chip: region_name region_offset_opt region_size region_list
+{
+	if (!(res = parse_descriptor($1, $2, $3, $4)))
+		YYABORT;
+};
+flash_region: region_name region_annotation_opt region_offset_opt
+						region_size_opt region_list_opt
+{
+	struct flashmap_descriptor *node = parse_descriptor($1, $3, $4, $5);
+	if (!node)
+		YYABORT;
+
+	char *annotation = $2;
+	if (annotation && !fmd_process_annotation_impl(node, annotation)) {
+		fprintf(stderr, "ERROR: Section '%s' has unexpected annotation '(%s)'\n",
+							node->name, annotation);
+		YYABORT;
+	}
+	free(annotation);
+
+	$$ = node;
+};
+region_name: STRING
+{
+	if (!$1) {
+		perror("ERROR: While allocating section name");
+		YYABORT;
+	}
+};
+region_annotation_opt: { $$ = NULL; }
+	| region_annotation;
+region_annotation: '(' STRING ')' { $$ = $2; };
+region_offset_opt: { $$ = (struct unsigned_option){false, 0}; }
+	| region_offset;
+region_offset: '@' INTEGER { $$ = (struct unsigned_option){true, $2}; };
+region_size_opt: { $$ = (struct unsigned_option){false, 0}; }
+	| region_size;
+region_size: INTEGER { $$ = (struct unsigned_option){true, $1}; };
+region_list_opt:
+{
+	$$ = (struct descriptor_list)
+					{.len = 0, .head = NULL, .tail = NULL};
+}
+	| region_list;
+region_list: '{' region_list_entries '}' { $$ = $2; };
+region_list_entries: flash_region
+{
+	struct descriptor_node *node = malloc(sizeof(*node));
+	if (!node) {
+		perror("ERROR: While allocating linked list node");
+		YYABORT;
+	}
+	node->val = $1;
+	node->next = NULL;
+	$$ = (struct descriptor_list){.len = 1, .head = node, .tail = node};
+}
+	| region_list_entries flash_region
+{
+	struct descriptor_node *node = malloc(sizeof(*node));
+	if (!node) {
+		perror("ERROR: While allocating linked list node");
+		YYABORT;
+	}
+	node->val = $2;
+	node->next = NULL;
+
+	$1.tail->next = node;
+	$$ = (struct descriptor_list)
+			{.len = $1.len + 1, .head = $1.head, .tail = node};
+};
+
+%%
+
+struct flashmap_descriptor *parse_descriptor(char *name,
+	struct unsigned_option offset, struct unsigned_option size,
+					struct descriptor_list children)
+{
+	struct flashmap_descriptor *region = malloc(sizeof(*region));
+	if (!region) {
+		perror("ERROR: While allocating descriptor section");
+		return NULL;
+	}
+	region->name = name;
+	region->offset_known = offset.val_known;
+	region->offset = offset.val;
+	region->size_known = size.val_known;
+	region->size = size.val;
+	region->list_len = children.len;
+	if (region->list_len) {
+		region->list = malloc(region->list_len * sizeof(*region->list));
+		if (!region->list) {
+			perror("ERROR: While allocating node children array");
+			return NULL;
+		}
+		struct descriptor_node *cur_node = children.head;
+		for (unsigned idx = 0; idx < region->list_len; ++idx) {
+			region->list[idx] = cur_node->val;
+
+			struct descriptor_node *next_node = cur_node->next;
+			free(cur_node);
+			cur_node = next_node;
+		}
+	} else {
+		region->list = NULL;
+	}
+	return region;
+}
+
+void yyerror(const char *s)
+{
+	fprintf(stderr, "%s\n", s);
+}