This patch adds a proof of concept framework to implement schema checker
using a combined C and DTSS based approach.

Complex and generic bindings can be implemented directly in C and then
instantiated from simple device-specific bindings using DTS-like DTSS
language.

This is based on Stephen Warren's C based DT schema checker proof of
concept patch.

[original C based DT schema checker proof of concept]
Signed-off-by: Stephen Warren <swar...@wwwdotorg.org>
[further development into hybrid solution]
Signed-off-by: Tomasz Figa <t.f...@samsung.com>
---
 Makefile         |   2 +-
 Makefile.dtc     |   5 +-
 checks.c         |  15 +++
 dtc.c            |  17 ++-
 dtc.h            |  26 +++++
 dtss-lexer.l     | 291 +++++++++++++++++++++++++++++++++++++++++++++++
 dtss-parser.y    | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 schemas/schema.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++
 schemas/schema.h |  89 +++++++++++++++
 srcpos.h         |   2 +
 treesource.c     |  22 ++++
 11 files changed, 1117 insertions(+), 4 deletions(-)
 create mode 100644 dtss-lexer.l
 create mode 100644 dtss-parser.y
 create mode 100644 schemas/schema.c
 create mode 100644 schemas/schema.h

diff --git a/Makefile b/Makefile
index 86f5ab3..0625fb8 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ EXTRAVERSION =
 LOCAL_VERSION =
 CONFIG_LOCALVERSION =
 
-CPPFLAGS = -I libfdt -I .
+CPPFLAGS = -I libfdt -I . -I schemas
 WARNINGS = -Werror -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
        -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wshadow
 CFLAGS = -g -Os -fPIC -Werror $(WARNINGS)
diff --git a/Makefile.dtc b/Makefile.dtc
index bece49b..bf19564 100644
--- a/Makefile.dtc
+++ b/Makefile.dtc
@@ -12,7 +12,8 @@ DTC_SRCS = \
        livetree.c \
        srcpos.c \
        treesource.c \
-       util.c
+       util.c \
+       schemas/schema.c
 
-DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
+DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c dtss-lexer.lex.c 
dtss-parser.tab.c
 DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
diff --git a/checks.c b/checks.c
index 47eda65..7c85fcf 100644
--- a/checks.c
+++ b/checks.c
@@ -19,6 +19,7 @@
  */
 
 #include "dtc.h"
+#include "schemas/schema.h"
 
 #ifdef TRACE_CHECKS
 #define TRACE(c, ...) \
@@ -651,6 +652,18 @@ static void 
check_obsolete_chosen_interrupt_controller(struct check *c,
 }
 TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
 
+/*
+ * Schema checks
+ */
+
+static void check_schema(struct check *c, struct node *dt,
+                                      struct node *node)
+{
+       if (schema_check_node(dt, node))
+               FAIL(c, "Schema check failed for %s", node->fullpath);
+}
+NODE_ERROR(schema, NULL);
+
 static struct check *check_table[] = {
        &duplicate_node_names, &duplicate_property_names,
        &node_name_chars, &node_name_format, &property_name_chars,
@@ -669,6 +682,8 @@ static struct check *check_table[] = {
        &avoid_default_addr_size,
        &obsolete_chosen_interrupt_controller,
 
+       &schema,
+
        &always_fail,
 };
 
diff --git a/dtc.c b/dtc.c
index d36ccdc..1a5913b 100644
--- a/dtc.c
+++ b/dtc.c
@@ -20,6 +20,7 @@
 
 #include "dtc.h"
 #include "srcpos.h"
+#include "schemas/schema.h"
 
 /*
  * Command line options
@@ -49,7 +50,7 @@ static void fill_fullpaths(struct node *tree, const char 
*prefix)
 
 /* Usage related data. */
 static const char usage_synopsis[] = "dtc [options] <input file>";
-static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv";
+static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:x:hv";
 static struct option const usage_long_opts[] = {
        {"quiet",            no_argument, NULL, 'q'},
        {"in-format",         a_argument, NULL, 'I'},
@@ -67,6 +68,7 @@ static struct option const usage_long_opts[] = {
        {"phandle",           a_argument, NULL, 'H'},
        {"warning",           a_argument, NULL, 'W'},
        {"error",             a_argument, NULL, 'E'},
+       {"schema",            a_argument, NULL, 'x'},
        {"help",             no_argument, NULL, 'h'},
        {"version",          no_argument, NULL, 'v'},
        {NULL,               no_argument, NULL, 0x0},
@@ -97,6 +99,7 @@ static const char * const usage_opts_help[] = {
         "\t\tboth   - Both \"linux,phandle\" and \"phandle\" properties",
        "\n\tEnable/disable warnings (prefix with \"no-\")",
        "\n\tEnable/disable errors (prefix with \"no-\")",
+       "\n\tUse schema file"
        "\n\tPrint this help and exit",
        "\n\tPrint version and exit",
        NULL,
@@ -105,10 +108,12 @@ static const char * const usage_opts_help[] = {
 int main(int argc, char *argv[])
 {
        struct boot_info *bi;
+       struct boot_info *bi_schema;
        const char *inform = "dts";
        const char *outform = "dts";
        const char *outname = "-";
        const char *depname = NULL;
+       const char *schema = NULL;
        bool force = false, sort = false;
        const char *arg;
        int opt;
@@ -185,6 +190,10 @@ int main(int argc, char *argv[])
                        parse_checks_option(false, true, optarg);
                        break;
 
+               case 'x':
+                       schema = optarg;
+                       break;
+
                case 'h':
                        usage(NULL);
                default:
@@ -220,6 +229,12 @@ int main(int argc, char *argv[])
        else
                die("Unknown input format \"%s\"\n", inform);
 
+       if (schema) {
+               bi_schema = schema_from_source(schema);
+               //dt_to_source(stdout, bi_schema);
+               build_schema_list(bi_schema);
+       }
+
        if (depfile) {
                fputc('\n', depfile);
                fclose(depfile);
diff --git a/dtc.h b/dtc.h
index 9ce9d12..19d2d24 100644
--- a/dtc.h
+++ b/dtc.h
@@ -135,21 +135,43 @@ struct label {
        struct label *next;
 };
 
+enum {
+       PROPERTY_DATA,
+       PROPERTY_USE,
+       PROPERTY_REQUIRE,
+       PROPERTY_MATCH,
+};
+
+#define PROPERTY_FLAG_OPTIONAL (1 << 0)
+
 struct property {
        bool deleted;
        char *name;
        struct data val;
+       unsigned int type;
+       unsigned int flags;
 
        struct property *next;
 
        struct label *labels;
 };
 
+enum {
+       NODE_DATA,
+       NODE_USE,
+       NODE_REQUIRE,
+};
+
+#define NODE_FLAG_OPTIONAL     (1 << 0)
+#define NODE_FLAG_INCOMPLETE   (1 << 1)
+
 struct node {
        bool deleted;
        char *name;
        struct property *proplist;
        struct node *children;
+       int type;
+       unsigned int flags;
 
        struct node *parent;
        struct node *next_sibling;
@@ -297,4 +319,8 @@ struct boot_info *dt_from_source(const char *f);
 
 struct boot_info *dt_from_fs(const char *dirname);
 
+/* Schema source */
+
+struct boot_info *schema_from_source(const char *fname);
+
 #endif /* _DTC_H */
diff --git a/dtss-lexer.l b/dtss-lexer.l
new file mode 100644
index 0000000..aee41f6
--- /dev/null
+++ b/dtss-lexer.l
@@ -0,0 +1,291 @@
+/*
+ * (C) Copyright David Gibson <d...@au1.ibm.com>, IBM Corporation.  2005.
+ *
+ *
+ * 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ *                                                                   USA
+ */
+
+%option noyywrap nounput noinput never-interactive prefix="dtss_yy"
+
+%x INCLUDE
+%x BYTESTRING
+%x PROPNODENAME
+%s V1
+
+PROPNODECHAR   [a-zA-Z0-9,._+*$#?^@-]
+PATHCHAR       ({PROPNODECHAR}|[/])
+LABEL          [a-zA-Z_][a-zA-Z0-9_]*
+STRING         \"([^\\"]|\\.)*\"
+CHAR_LITERAL   '([^']|\\')*'
+WS             [[:space:]]
+COMMENT                "/*"([^*]|\*+[^*/])*\*+"/"
+LINECOMMENT    "//".*\n
+
+%{
+#include "dtc.h"
+#include "srcpos.h"
+
+#define DTSS_YYLTYPE struct srcpos
+
+#include "dtss-parser.tab.h"
+
+DTSS_YYLTYPE dtss_yylloc;
+extern bool treesource_error;
+
+/* CAUTION: this will stop working if we ever use dtss_yyless() or 
dtss_yyunput() */
+#define        YY_USER_ACTION \
+       { \
+               srcpos_update(&dtss_yylloc, dtss_yytext, dtss_yyleng); \
+       }
+
+/* #define LEXDEBUG    1 */
+
+#ifdef LEXDEBUG
+#define DPRINT(fmt, ...)       fprintf(stderr, fmt, ##__VA_ARGS__)
+#else
+#define DPRINT(fmt, ...)       do { } while (0)
+#endif
+
+static int dts_version = 1;
+
+#define BEGIN_DEFAULT()                DPRINT("<V1>\n"); \
+                               BEGIN(V1); \
+
+static void push_input_file(const char *filename);
+static bool pop_input_file(void);
+static void lexical_error(const char *fmt, ...);
+%}
+
+%%
+<*>"/include/"{WS}*{STRING} {
+                       char *name = strchr(dtss_yytext, '\"') + 1;
+                       dtss_yytext[dtss_yyleng-1] = '\0';
+                       push_input_file(name);
+               }
+
+<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? {
+                       char *line, *tmp, *fn;
+                       /* skip text before line # */
+                       line = dtss_yytext;
+                       while (!isdigit(*line))
+                               line++;
+                       /* skip digits in line # */
+                       tmp = line;
+                       while (!isspace(*tmp))
+                               tmp++;
+                       /* "NULL"-terminate line # */
+                       *tmp = '\0';
+                       /* start of filename */
+                       fn = strchr(tmp + 1, '"') + 1;
+                       /* strip trailing " from filename */
+                       tmp = strchr(fn, '"');
+                       *tmp = 0;
+                       /* -1 since #line is the number of the next line */
+                       srcpos_set_line(xstrdup(fn), atoi(line) - 1);
+               }
+
+<*><<EOF>>             {
+                       if (!pop_input_file()) {
+                               yyterminate();
+                       }
+               }
+
+<*>{STRING}    {
+                       DPRINT("String: %s\n", dtss_yytext);
+                       dtss_yylval.data = 
data_copy_escape_string(dtss_yytext+1,
+                                       dtss_yyleng-2);
+                       return DT_STRING;
+               }
+
+<*>"/dtss-v1/" {
+                       DPRINT("Keyword: /dtss-v1/\n");
+                       dts_version = 0x8001;
+                       BEGIN_DEFAULT();
+                       return DTSS_V1;
+               }
+
+<*>"/bits/"    {
+                       DPRINT("Keyword: /bits/\n");
+                       BEGIN_DEFAULT();
+                       return DT_BITS;
+               }
+
+<*>"/delete-property/" {
+                       DPRINT("Keyword: /delete-property/\n");
+                       DPRINT("<PROPNODENAME>\n");
+                       BEGIN(PROPNODENAME);
+                       return DT_DEL_PROP;
+               }
+
+<*>"/delete-node/"     {
+                       DPRINT("Keyword: /delete-node/\n");
+                       DPRINT("<PROPNODENAME>\n");
+                       BEGIN(PROPNODENAME);
+                       return DT_DEL_NODE;
+               }
+
+<*>"/match/"   {
+                       DPRINT("Keyword: /match/\n");
+                       return DTSS_MATCH;
+               }
+
+<*>"/require/" {
+                       DPRINT("Keyword: /require/\n");
+                       return DTSS_REQUIRE;
+               }
+
+<*>"/use/"     {
+                       DPRINT("Keyword: /use/\n");
+                       return DTSS_USE;
+               }
+
+<*>"/incomplete/"      {
+                       DPRINT("Keyword: /incomplete/\n");
+                       return DTSS_INCOMPLETE;
+               }
+
+<*>"/optional/"        {
+                       DPRINT("Keyword: /optional/\n");
+                       return DTSS_OPTIONAL;
+               }
+
+<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
+                       char *e;
+                       DPRINT("Integer Literal: '%s'\n", yytext);
+
+                       errno = 0;
+                       dtss_yylval.integer = strtoull(yytext, &e, 0);
+
+                       assert(!(*e) || !e[strspn(e, "UL")]);
+
+                       if (errno == ERANGE)
+                               lexical_error("Integer literal '%s' out of 
range",
+                                             yytext);
+                       else
+                               /* ERANGE is the only strtoull error triggerable
+                                *  by strings matching the pattern */
+                               assert(errno == 0);
+                       return DT_LITERAL;
+               }
+
+<*>{CHAR_LITERAL}      {
+                       struct data d;
+                       DPRINT("Character literal: %s\n", yytext);
+
+                       d = data_copy_escape_string(yytext+1, yyleng-2);
+                       if (d.len == 1) {
+                               lexical_error("Empty character literal");
+                               dtss_yylval.integer = 0;
+                               return DT_CHAR_LITERAL;
+                       }
+
+                       dtss_yylval.integer = (unsigned char)d.val[0];
+
+                       if (d.len > 2)
+                               lexical_error("Character literal has %d"
+                                             " characters instead of 1",
+                                             d.len - 1);
+
+                       return DT_CHAR_LITERAL;
+               }
+
+<*>\&{LABEL}   {       /* label reference */
+                       DPRINT("Ref: %s\n", dtss_yytext+1);
+                       dtss_yylval.labelref = xstrdup(dtss_yytext+1);
+                       return DT_REF;
+               }
+
+<*>"&{/"{PATHCHAR}*\}  {       /* new-style path reference */
+                       dtss_yytext[dtss_yyleng-1] = '\0';
+                       DPRINT("Ref: %s\n", dtss_yytext+2);
+                       dtss_yylval.labelref = xstrdup(dtss_yytext+2);
+                       return DT_REF;
+               }
+
+<BYTESTRING>[0-9a-fA-F]{2} {
+                       dtss_yylval.byte = strtol(dtss_yytext, NULL, 16);
+                       DPRINT("Byte: %02x\n", (int)dtss_yylval.byte);
+                       return DT_BYTE;
+               }
+
+<BYTESTRING>"]"        {
+                       DPRINT("/BYTESTRING\n");
+                       BEGIN_DEFAULT();
+                       return ']';
+               }
+
+<PROPNODENAME>\\?{PROPNODECHAR}+ {
+                       DPRINT("PropNodeName: %s\n", dtss_yytext);
+                       dtss_yylval.propnodename = xstrdup((dtss_yytext[0] == 
'\\') ?
+                                                       dtss_yytext + 1 : 
dtss_yytext);
+                       BEGIN_DEFAULT();
+                       return DT_PROPNODENAME;
+               }
+
+<*>{WS}+       /* eat whitespace */
+<*>{COMMENT}+  /* eat C-style comments */
+<*>{LINECOMMENT}+ /* eat C++-style comments */
+
+<*>.           {
+                       DPRINT("Char: %c (\\x%02x)\n", dtss_yytext[0],
+                               (unsigned)dtss_yytext[0]);
+                       if (dtss_yytext[0] == '[') {
+                               DPRINT("<BYTESTRING>\n");
+                               BEGIN(BYTESTRING);
+                       }
+                       if ((dtss_yytext[0] == '{')
+                           || (dtss_yytext[0] == ';')) {
+                               DPRINT("<PROPNODENAME>\n");
+                               BEGIN(PROPNODENAME);
+                       }
+                       return dtss_yytext[0];
+               }
+
+%%
+
+static void push_input_file(const char *filename)
+{
+       assert(filename);
+
+       srcfile_push(filename);
+
+       dtss_yyin = current_srcfile->f;
+
+       dtss_yypush_buffer_state(dtss_yy_create_buffer(dtss_yyin, YY_BUF_SIZE));
+}
+
+
+static bool pop_input_file(void)
+{
+       if (srcfile_pop() == 0)
+               return false;
+
+       dtss_yypop_buffer_state();
+       dtss_yyin = current_srcfile->f;
+
+       return true;
+}
+
+static void lexical_error(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       srcpos_verror(&dtss_yylloc, "Lexical error", fmt, ap);
+       va_end(ap);
+
+       treesource_error = true;
+}
diff --git a/dtss-parser.y b/dtss-parser.y
new file mode 100644
index 0000000..1c807da
--- /dev/null
+++ b/dtss-parser.y
@@ -0,0 +1,341 @@
+/*
+ * (C) Copyright David Gibson <d...@au1.ibm.com>, IBM Corporation.  2005.
+ *
+ *
+ * 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ *                                                                   USA
+ */
+
+%define api.prefix dtss_yy
+
+%{
+#include <stdio.h>
+
+#include "dtc.h"
+#include "srcpos.h"
+
+#define DTSS_YYLTYPE struct srcpos
+
+extern int dtss_yylex(void);
+extern void dtss_yyerror(char const *s);
+#define ERROR(loc, ...) \
+       do { \
+               srcpos_error((loc), "Error", __VA_ARGS__); \
+               treesource_error = true; \
+       } while (0)
+
+extern struct boot_info *the_boot_info;
+extern bool treesource_error;
+%}
+
+%union {
+       char *propnodename;
+       char *labelref;
+       unsigned int cbase;
+       uint8_t byte;
+       struct data data;
+
+       struct {
+               struct data     data;
+               int             bits;
+       } array;
+
+       struct property *prop;
+       struct property *proplist;
+       struct node *node;
+       struct node *nodelist;
+       struct reserve_info *re;
+       uint64_t integer;
+}
+
+%token DTSS_V1
+%token DT_BITS
+%token DT_DEL_PROP
+%token DT_DEL_NODE
+%token DTSS_INCOMPLETE
+%token DTSS_MATCH
+%token DTSS_USE
+%token DTSS_REQUIRE
+%token DTSS_OPTIONAL
+%token <propnodename> DT_PROPNODENAME
+%token <integer> DT_LITERAL
+%token <integer> DT_CHAR_LITERAL
+%token <cbase> DT_BASE
+%token <byte> DT_BYTE
+%token <data> DT_STRING
+%token <data> DTSS_TYPESPEC
+%token <labelref> DT_LABEL
+%token <labelref> DT_REF
+%token DT_INCBIN
+
+%type <data> propdata
+%type <data> propdataprefix
+%type <array> arrayprefix
+%type <data> bytestring
+%type <prop> propdef
+%type <proplist> proplist
+
+%type <node> schema
+%type <node> schemaroot
+%type <node> nodedef
+%type <node> subnode
+%type <nodelist> subnodes
+
+%type <integer> integer_prim
+
+%%
+
+sourcefile:
+         DTSS_V1 ';' schema
+               {
+                       the_boot_info = build_boot_info(NULL, $3, 0);
+               }
+       ;
+
+schemaroot:
+       subnodes
+               {
+                       $$ = build_node(NULL, $1);
+               }
+       ;
+
+schema:
+       schemaroot
+               {
+                       $$ = name_node($1, "/");
+               }
+       ;
+
+nodedef:
+         '{' proplist subnodes '}' ';'
+               {
+                       $$ = build_node($2, $3);
+               }
+       ;
+
+proplist:
+         /* empty */
+               {
+                       $$ = NULL;
+               }
+       | proplist propdef
+               {
+                       $$ = chain_property($2, $1);
+               }
+       ;
+
+propdef:
+         DT_PROPNODENAME '=' propdata ';'
+               {
+                       $$ = build_property($1, $3);
+               }
+       | DT_PROPNODENAME ';'
+               {
+                       $$ = build_property($1, empty_data);
+               }
+       | DT_DEL_PROP DT_PROPNODENAME ';'
+               {
+                       $$ = build_property_delete($2);
+               }
+       | DTSS_MATCH propdef
+               {
+                       $2->type = PROPERTY_MATCH;
+                       $$ = $2;
+               }
+       | DTSS_USE propdef
+               {
+                       $2->type = PROPERTY_USE;
+                       $$ = $2;
+               }
+       | DTSS_REQUIRE propdef
+               {
+                       $2->type = PROPERTY_REQUIRE;
+                       $$ = $2;
+               }
+       | DTSS_OPTIONAL propdef
+               {
+                       $2->flags |= PROPERTY_FLAG_OPTIONAL;
+                       $$ = $2;
+               }
+       ;
+
+propdata:
+         propdataprefix DT_STRING
+               {
+                       $$ = data_merge($1, $2);
+               }
+       | propdataprefix arrayprefix '>'
+               {
+                       $$ = data_merge($1, $2.data);
+               }
+       | propdataprefix '[' bytestring ']'
+               {
+                       $$ = data_merge($1, $3);
+               }
+       | propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' 
integer_prim ')'
+               {
+                       FILE *f = srcfile_relative_open($4.val, NULL);
+                       struct data d;
+
+                       if ($6 != 0)
+                               if (fseek(f, $6, SEEK_SET) != 0)
+                                       die("Couldn't seek to offset %llu in 
\"%s\": %s",
+                                           (unsigned long long)$6, $4.val,
+                                           strerror(errno));
+
+                       d = data_copy_file(f, $8);
+
+                       $$ = data_merge($1, d);
+                       fclose(f);
+               }
+       | propdataprefix DT_INCBIN '(' DT_STRING ')'
+               {
+                       FILE *f = srcfile_relative_open($4.val, NULL);
+                       struct data d = empty_data;
+
+                       d = data_copy_file(f, -1);
+
+                       $$ = data_merge($1, d);
+                       fclose(f);
+               }
+       ;
+
+propdataprefix:
+         /* empty */
+               {
+                       $$ = empty_data;
+               }
+       | propdata ','
+               {
+                       $$ = $1;
+               }
+       | propdataprefix DT_LABEL
+               {
+                       $$ = data_add_marker($1, LABEL, $2);
+               }
+       ;
+
+arrayprefix:
+       DT_BITS DT_LITERAL '<'
+               {
+                       unsigned long long bits;
+
+                       bits = $2;
+
+                       if ((bits !=  8) && (bits != 16) &&
+                           (bits != 32) && (bits != 64)) {
+                               ERROR(&@2, "Array elements must be"
+                                     " 8, 16, 32 or 64-bits");
+                               bits = 32;
+                       }
+
+                       $$.data = empty_data;
+                       $$.bits = bits;
+               }
+       | '<'
+               {
+                       $$.data = empty_data;
+                       $$.bits = 32;
+               }
+       | arrayprefix integer_prim
+               {
+                       if ($1.bits < 64) {
+                               uint64_t mask = (1ULL << $1.bits) - 1;
+                               /*
+                                * Bits above mask must either be all zero
+                                * (positive within range of mask) or all one
+                                * (negative and sign-extended). The second
+                                * condition is true if when we set all bits
+                                * within the mask to one (i.e. | in the
+                                * mask), all bits are one.
+                                */
+                               if (($2 > mask) && (($2 | mask) != -1ULL))
+                                       ERROR(&@2, "Value out of range for"
+                                             " %d-bit array element", $1.bits);
+                       }
+
+                       $$.data = data_append_integer($1.data, $2, $1.bits);
+               }
+       ;
+
+integer_prim:
+         DT_LITERAL
+       | DT_CHAR_LITERAL
+       ;
+
+bytestring:
+         /* empty */
+               {
+                       $$ = empty_data;
+               }
+       | bytestring DT_BYTE
+               {
+                       $$ = data_append_byte($1, $2);
+               }
+       ;
+
+subnodes:
+         /* empty */
+               {
+                       $$ = NULL;
+               }
+       | subnode subnodes
+               {
+                       $$ = chain_node($1, $2);
+               }
+       | subnode propdef
+               {
+                       ERROR(&@2, "Properties must precede subnodes");
+                       YYERROR;
+               }
+       ;
+
+subnode:
+         DT_PROPNODENAME nodedef
+               {
+                       $$ = name_node($2, $1);
+               }
+       | DT_DEL_NODE DT_PROPNODENAME ';'
+               {
+                       $$ = name_node(build_node_delete(), $2);
+               }
+       | DTSS_USE subnode
+               {
+                       $2->type = NODE_USE;
+                       $$ = $2;
+               }
+       | DTSS_REQUIRE subnode
+               {
+                       $2->type = NODE_REQUIRE;
+                       $$ = $2;
+               }
+       | DTSS_OPTIONAL subnode
+               {
+                       $2->flags |= NODE_FLAG_OPTIONAL;
+                       $$ = $2;
+               }
+       | DTSS_INCOMPLETE subnode
+               {
+                       $2->flags |= NODE_FLAG_INCOMPLETE;
+                       $$ = $2;
+               }
+       ;
+
+%%
+
+void dtss_yyerror(char const *s)
+{
+       ERROR(&yylloc, "%s", s);
+}
diff --git a/schemas/schema.c b/schemas/schema.c
new file mode 100644
index 0000000..e5258cf
--- /dev/null
+++ b/schemas/schema.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2013 Stephen Warren <swar...@wwwdotorg.org>
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ *     Tomasz Figa <t.f...@samsung.com>
+ *
+ * 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ *                                                                   USA
+ */
+
+#include "schema.h"
+
+static struct schema_checker schema_list = {
+       .next = &schema_list,
+};
+
+int schema_check_node(struct node *root, struct node *node)
+{
+       const struct schema_checker *checker;
+       int match;
+       int checked = 0;
+
+       checker = schema_list.next;
+       for (; checker != &schema_list; checker = checker->next) {
+               match = checker->matchfn(node, checker);
+               if (!match)
+                       continue;
+
+               pr_info("Node %s matches checker %s at level %d\n",
+                       node->fullpath, checker->name, match);
+
+               checker->checkfn(root, node, checker);
+               checked = 1;
+       }
+
+       /*
+        * FIXME: this is too noisy right now. Make it optional until schemas
+        * for most bindings are implemented.
+        */
+       if (!checked) {
+               pr_warn("no schema for node %s\n", node->fullpath);
+               return 0;
+       }
+
+       /*
+        * FIXME: grab validation state from global somewhere.
+        * Using global state avoids having check return values after every
+        * function call, thus making the code less verbose and appear more
+        * assertion-based.
+        */
+       return 0;
+}
+
+int schema_match_path(struct node *node, const struct schema_checker *checker)
+{
+       return !strcmp(node->fullpath, checker->u.path.path);
+}
+
+int schema_match_compatible(struct node *node,
+                               const struct schema_checker *checker)
+{
+       struct property *compat_prop;
+       int index;
+       const char *node_compat;
+       const char **test_compats;
+
+       compat_prop = get_property(node, "compatible");
+       if (!compat_prop)
+               return 0;
+
+       /*
+        * Match with any compatible value of schema with any compatible
+        * value of node being verified.
+        */
+       for (node_compat = compat_prop->val.val, index = 0;
+                       *node_compat;
+                       node_compat += strlen(node_compat) + 1, index++) {
+               for (test_compats = checker->u.compatible.compats;
+                               *test_compats; test_compats++) {
+                       if (!strcmp(node_compat, *test_compats))
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+struct property *schema_get_param(struct node *params, const char *name)
+{
+       if (!params)
+               return NULL;
+
+       return get_property(params, name);
+}
+
+int schema_get_param_cell(struct node *params, const char *name, cell_t *val)
+{
+       struct property *prop;
+
+       prop = schema_get_param(params, name);
+       if (!prop)
+               return -ENOENT;
+
+       if (!prop->val.len)
+               return -EINVAL;
+
+       *val = propval_cell(prop);
+       return 0;
+}
+
+struct property *require_property(struct node *node, const char *name)
+{
+       struct property *prop;
+
+       prop = get_property(node, name);
+       if (!prop) {
+               /*
+                * FIXME: set global error state. The same comment applies
+                * everywhere.
+                */
+               pr_err("node '%s' missing '%s' property\n",
+                       node->fullpath, name);
+       }
+
+       return prop;
+}
+
+static void check_required_property(struct node *node, struct property *schema)
+{
+       struct property *prop;
+
+       prop = require_property(node, schema->name);
+       if (!prop)
+               return;
+
+       if (schema->val.len
+           && (schema->val.len != prop->val.len
+           || memcmp(schema->val.val, prop->val.val, prop->val.len)))
+               pr_err("node %s with wrong constant value of property %s\n",
+                       node->fullpath, schema->name);
+}
+
+static void check_optional_property(struct node *node, struct property *schema)
+{
+       struct property *prop;
+
+       prop = get_property(node, schema->name);
+       if (!prop)
+               return;
+
+       check_required_property(node, schema);
+}
+
+/*
+ * FIXME: Use a more generic solution, which does not rely on linker
+ * specific features.
+ */
+extern const struct generic_schema __start_generic_schemas;
+extern const struct generic_schema __stop_generic_schemas;
+
+static void check_generic_schema(struct node *root, struct node *node,
+                                       const char *name,
+                                       struct node *schema_node,
+                                       bool required)
+{
+       const struct generic_schema *gs;
+       int i;
+       bool checked = false;
+       unsigned int count = &__stop_generic_schemas - &__start_generic_schemas;
+
+       pr_info("running schema \"%s\"\n", name);
+
+       gs = &__start_generic_schemas;
+       for (i = 0; i < count; ++i, ++gs) {
+               if (strcmp(gs->name, name))
+                       continue;
+
+               gs->checkfn(gs, root, node, schema_node, required);
+
+               checked = true;
+       }
+
+       if (!checked)
+               pr_err("schema \"%s\" not found\n", name);
+}
+
+static void check_dtss_schema(struct node *root, struct node *node,
+                             const struct schema_checker *checker)
+{
+       struct property *prop_schema;
+       struct node *schema = checker->node, *node_schema;
+
+       for_each_property(schema, prop_schema) {
+               if (!strcmp(prop_schema->name, "compatible")
+                   || !strcmp(prop_schema->name, "device_type"))
+                       continue;
+
+               switch (prop_schema->type) {
+               case PROPERTY_DATA:
+                       if (prop_schema->flags & PROPERTY_FLAG_OPTIONAL)
+                               check_optional_property(node, prop_schema);
+                       else
+                               check_required_property(node, prop_schema);
+                       break;
+
+               case PROPERTY_REQUIRE:
+                       check_generic_schema(root, node, prop_schema->name,
+                                               NULL, true);
+                       break;
+
+               case PROPERTY_USE:
+                       check_generic_schema(root, node, prop_schema->name,
+                                               NULL, false);
+                       break;
+               }
+       }
+
+       for_each_child(schema, node_schema) {
+               switch (node_schema->type) {
+               case NODE_DATA:
+                       /* TODO: verify subnodes */
+                       break;
+
+               case NODE_REQUIRE:
+                       check_generic_schema(root, node, node_schema->name,
+                                               node_schema, true);
+                       break;
+
+               case NODE_USE:
+                       check_generic_schema(root, node, node_schema->name,
+                                               node_schema, false);
+                       break;
+               }
+       }
+
+       /* TODO: detect unknown properties */
+}
+
+void build_schema_list(struct boot_info *schema_tree)
+{
+       struct node *root, *schema;
+
+       root = get_node_by_path(schema_tree->dt, "/");
+       if (!root) {
+               pr_err("schema file missing / node\n");
+               return;
+       }
+
+       for_each_child(root, schema) {
+               struct schema_checker *sc = xmalloc(sizeof(*sc));
+               struct property *prop;
+
+               sc->node = schema;
+               sc->checkfn = check_dtss_schema;
+               sc->name = schema->name;
+
+               for_each_property(schema, prop)
+                       if (prop->type == PROPERTY_MATCH)
+                               goto found_match;
+
+               pr_err("schema '%s' without matching key\n", sc->name);
+               free(sc);
+               continue;
+
+found_match:
+               if (!strcmp(prop->name, "compatible")) {
+                       int count;
+                       const char **compats;
+                       const char *compat;
+
+                       count = propval_string_count(schema, prop) + 1;
+                       compats = xmalloc(count * sizeof(*compats));
+
+                       sc->u.compatible.compats = compats;
+
+                       while ((compat = propval_next_string(prop, compat))) {
+                               *compats = compat;
+                               ++compats;
+                       }
+                       *compats = NULL;
+
+                       sc->matchfn = schema_match_compatible;
+                       sc->next = schema_list.next;
+                       schema_list.next = sc;
+               } else if (!strcmp(prop->name, "device_type")) {
+                       sc->u.type.type = propval_next_string(prop, NULL);
+                       sc->next = schema_list.next;
+                       schema_list.next = sc;
+               } else if (!strcmp(prop->name, "path")) {
+                       sc->u.path.path = propval_next_string(prop, NULL);
+                       sc->matchfn = schema_match_path;
+                       sc->next = schema_list.next;
+                       schema_list.next = sc;
+               } else {
+                       pr_err("wrong schema key type\n");
+                       free(sc);
+               }
+       }
+}
diff --git a/schemas/schema.h b/schemas/schema.h
new file mode 100644
index 0000000..9972a17
--- /dev/null
+++ b/schemas/schema.h
@@ -0,0 +1,89 @@
+#ifndef _SCHEMAS_SCHEMA_H
+#define _SCHEMAS_SCHEMA_H
+
+#include "dtc.h"
+
+struct schema_checker;
+
+typedef int (schema_matcher_func)(struct node *node,
+                                       const struct schema_checker *checker);
+typedef void (schema_checker_func)(struct node *root, struct node *node,
+                                       const struct schema_checker *checker);
+
+struct schema_checker {
+       const char *name;
+       schema_matcher_func *matchfn;
+       schema_checker_func *checkfn;
+       struct node *node;
+       union {
+               struct {
+                       const char *path;
+               } path;
+               struct {
+                       const char **compats;
+               } compatible;
+               struct {
+                       const char *type;
+               } type;
+       } u;
+       struct schema_checker *next;
+};
+
+int schema_check_node(struct node *root, struct node *node);
+
+int schema_match_path(struct node *node, const struct schema_checker *checker);
+int schema_match_compatible(struct node *node,
+                               const struct schema_checker *checker);
+
+#define SCHEMA_MATCH_PATH(_name_, _path_) \
+       struct schema_checker schema_checker_##_name_ = { \
+               .name = #_name_, \
+               .matchfn = schema_match_path, \
+               .checkfn = checkfn_##_name_, \
+               .u.path.path = _path_, \
+       };
+
+#define SCHEMA_MATCH_COMPATIBLE(_name_) \
+       struct schema_checker schema_checker_##_name_ = { \
+               .name = #_name_, \
+               .matchfn = schema_match_compatible, \
+               .checkfn = checkfn_##_name_, \
+               .u.compatible.compats = compats_##_name_, \
+       };
+
+struct generic_schema;
+
+typedef void (generic_schema_checker_func)(const struct generic_schema *schema,
+                                          struct node *root, struct node *node,
+                                          struct node *params, bool required);
+
+struct generic_schema {
+       const char *name;
+       generic_schema_checker_func *checkfn;
+};
+
+#define __used         __attribute__((__used__))
+#define __section(S)   __attribute__ ((__section__(#S)))
+
+#define GENERIC_SCHEMA(_dt_name_, _name_)                              \
+       static const struct generic_schema generic_schema_##_name_      \
+               __used __section(generic_schemas) = {                   \
+                       .name = _dt_name_,                              \
+                       .checkfn = generic_checkfn_##_name_,            \
+       };
+
+struct property *require_property(struct node *node, const char *propname);
+struct property *schema_get_param(struct node *params, const char *name);
+int schema_get_param_cell(struct node *params, const char *name, cell_t *val);
+
+void build_schema_list(struct boot_info *schema_tree);
+
+#define schema_err(s,fmt,args...)      pr_err("%s: " fmt, (s)->name, ##args)
+#define schema_warn(s,fmt,args...)     pr_warn("%s: " fmt, (s)->name, ##args)
+#define schema_info(s,fmt,args...)     pr_info("%s: " fmt, (s)->name, ##args)
+
+#define node_err(n,fmt,args...)                pr_err("%s: " fmt, 
(n)->fullpath, ##args)
+#define node_warn(n,fmt,args...)       pr_warn("%s: " fmt, (n)->fullpath, 
##args)
+#define node_info(n,fmt,args...)       pr_info("%s: " fmt, (n)->fullpath, 
##args)
+
+#endif
diff --git a/srcpos.h b/srcpos.h
index f81827b..b761522 100644
--- a/srcpos.h
+++ b/srcpos.h
@@ -75,7 +75,9 @@ struct srcpos {
     struct srcfile_state *file;
 };
 
+#ifndef YYLTYPE
 #define YYLTYPE struct srcpos
+#endif
 
 #define YYLLOC_DEFAULT(Current, Rhs, N)                                        
        \
        do {                                                                    
\
diff --git a/treesource.c b/treesource.c
index bf7a626..e50285c 100644
--- a/treesource.c
+++ b/treesource.c
@@ -25,6 +25,10 @@ extern FILE *yyin;
 extern int yyparse(void);
 extern YYLTYPE yylloc;
 
+extern FILE *dtss_yyin;
+extern int dtss_yyparse(void);
+extern YYLTYPE dtss_yylloc;
+
 struct boot_info *the_boot_info;
 bool treesource_error;
 
@@ -46,6 +50,24 @@ struct boot_info *dt_from_source(const char *fname)
        return the_boot_info;
 }
 
+struct boot_info *schema_from_source(const char *fname)
+{
+       the_boot_info = NULL;
+       treesource_error = false;
+
+       srcfile_push(fname);
+       dtss_yyin = current_srcfile->f;
+       dtss_yylloc.file = current_srcfile;
+
+       if (dtss_yyparse() != 0)
+               die("Unable to parse input tree\n");
+
+       if (treesource_error)
+               die("Syntax error parsing input tree\n");
+
+       return the_boot_info;
+}
+
 static void write_prefix(FILE *f, int level)
 {
        int i;
-- 
1.8.5.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree-compiler" 
in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to