* [PATCH RFC 1/5] dtc: Add helpers for various message levels
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
@ 2014-02-20 18:06 ` Tomasz Figa
2014-03-09 11:43 ` David Gibson
2014-02-20 18:06 ` [PATCH RFC 2/5] dtc: livetree: Add more tree parsing helpers Tomasz Figa
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Tomasz Figa @ 2014-02-20 18:06 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds three helper macros to print errors, warnings and
informational messages using standard format.
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
---
dtc.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/dtc.h b/dtc.h
index 20de073..e95bed7 100644
--- a/dtc.h
+++ b/dtc.h
@@ -43,6 +43,9 @@
#define debug(fmt,args...)
#endif
+#define pr_err(...) fprintf (stderr, "ERROR: " __VA_ARGS__)
+#define pr_warn(...) fprintf (stderr, "WARNING: " __VA_ARGS__)
+#define pr_info(...) fprintf (stderr, "INFO: " __VA_ARGS__)
#define DEFAULT_FDT_VERSION 17
--
1.8.5.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 1/5] dtc: Add helpers for various message levels
2014-02-20 18:06 ` [PATCH RFC 1/5] dtc: Add helpers for various message levels Tomasz Figa
@ 2014-03-09 11:43 ` David Gibson
0 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2014-03-09 11:43 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Feb 20, 2014 at 07:06:47PM +0100, Tomasz Figa wrote:
> This patch adds three helper macros to print errors, warnings and
> informational messages using standard format.
>
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> ---
> dtc.h | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/dtc.h b/dtc.h
> index 20de073..e95bed7 100644
> --- a/dtc.h
> +++ b/dtc.h
> @@ -43,6 +43,9 @@
> #define debug(fmt,args...)
> #endif
>
> +#define pr_err(...) fprintf (stderr, "ERROR: " __VA_ARGS__)
> +#define pr_warn(...) fprintf (stderr, "WARNING: " __VA_ARGS__)
> +#define pr_info(...) fprintf (stderr, "INFO: " __VA_ARGS__)
So, there are already several message helper functions in dtc.
They're not all that consistently applied, so I can see an argument
for cleaning things up, and replacing them with ones modelled on the
kernel versions like these.
But.. this patch doesn't do that, it just adds yet another variant to
the mix.
In addition, what we want for messages in dtc is a bit different from
the kernel - the kernel's expexted to keep running, and the messages
log events of interest. For dtc, for each warning/error you should
also be deciding whether that should terminate the program or not -
these helpers don't address that.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140309/40f9c367/attachment-0001.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC 2/5] dtc: livetree: Add more tree parsing helpers
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
2014-02-20 18:06 ` [PATCH RFC 1/5] dtc: Add helpers for various message levels Tomasz Figa
@ 2014-02-20 18:06 ` Tomasz Figa
2014-03-09 12:03 ` David Gibson
2014-02-20 18:06 ` [PATCH RFC 3/5] Implement DT schema checker using hybrid approach Tomasz Figa
` (4 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Tomasz Figa @ 2014-02-20 18:06 UTC (permalink / raw)
To: linux-arm-kernel
This patch extends the set of parsing helpers available in dtc with
string and phandle parsing.
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
---
dtc.h | 28 ++++++++
livetree.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 258 insertions(+)
diff --git a/dtc.h b/dtc.h
index e95bed7..9ce9d12 100644
--- a/dtc.h
+++ b/dtc.h
@@ -163,6 +163,13 @@ struct node {
struct label *labels;
};
+#define MAX_PHANDLE_ARGS 16
+struct of_phandle_args {
+ struct node *np;
+ int args_count;
+ uint32_t args[MAX_PHANDLE_ARGS];
+};
+
#define for_each_label_withdel(l0, l) \
for ((l) = (l0); (l); (l) = (l)->next)
@@ -184,6 +191,11 @@ struct node {
for_each_child_withdel(n, c) \
if (!(c)->deleted)
+#define for_each_propval_string(p, s) \
+ for (s = propval_next_string(p, NULL); \
+ s; \
+ s = propval_next_string(p, s))
+
void add_label(struct label **labels, char *label);
void delete_labels(struct label **labels);
@@ -208,6 +220,22 @@ void delete_node(struct node *node);
const char *get_unitname(struct node *node);
struct property *get_property(struct node *node, const char *propname);
cell_t propval_cell(struct property *prop);
+int propval_string_count(struct node *np, struct property *prop);
+const char *propval_next_string(struct property *prop, const char *cur);
+int propval_match_string(struct property *prop, const char *string);
+struct node *propval_parse_phandle(struct node *root,
+ struct property *prop, int index);
+int propval_parse_phandle_with_args(struct node *root,
+ struct property *prop,
+ const char *cells_name, int index,
+ struct of_phandle_args *out_args);
+int propval_parse_phandle_with_fixed_args(struct node *root,
+ struct property *prop, int cell_count,
+ int index,
+ struct of_phandle_args *out_args);
+int propval_count_phandle_with_args(struct node *root,
+ struct property *prop,
+ const char *cells_name);
struct property *get_property_by_label(struct node *tree, const char *label,
struct node **node);
struct marker *get_marker_label(struct node *tree, const char *label,
diff --git a/livetree.c b/livetree.c
index b61465f..015ed06 100644
--- a/livetree.c
+++ b/livetree.c
@@ -377,6 +377,230 @@ cell_t propval_cell(struct property *prop)
return fdt32_to_cpu(*((cell_t *)prop->val.val));
}
+int propval_string_count(struct node *np, struct property *prop)
+{
+ int i = 0;
+ size_t l = 0, total = 0;
+ const char *p;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->val.val)
+ return -ENODATA;
+ if (strnlen(prop->val.val, prop->val.len) >= prop->val.len)
+ return -EILSEQ;
+
+ p = prop->val.val;
+
+ for (i = 0; total < prop->val.len; total += l, p += l, i++)
+ l = strlen(p) + 1;
+
+ return i;
+}
+
+const char *propval_next_string(struct property *prop, const char *cur)
+{
+ const char *curv = cur;
+
+ if (!prop)
+ return NULL;
+
+ if (!cur)
+ return prop->val.val;
+
+ curv += strlen(cur) + 1;
+ if (curv >= prop->val.val + prop->val.len)
+ return NULL;
+
+ return curv;
+}
+
+int propval_match_string(struct property *prop, const char *string)
+{
+ size_t l;
+ int i;
+ const char *p, *end;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->val.val)
+ return -ENODATA;
+
+ p = prop->val.val;
+ end = p + prop->val.len;
+
+ for (i = 0; p < end; i++, p += l) {
+ l = strlen(p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ if (strcmp(string, p) == 0)
+ return i; /* Found it; return index */
+ }
+ return -ENODATA;
+}
+
+static int parse_phandle_with_args(struct node *root,
+ struct property *prop,
+ const char *cells_name,
+ int cell_count, int index,
+ struct of_phandle_args *out_args)
+{
+ const cell_t *list, *list_end;
+ int rc = 0, size, cur_index = 0;
+ uint32_t count = 0;
+ struct node *node = NULL;
+ cell_t phandle;
+
+ /* Retrieve the phandle list property */
+ size = prop->val.len;
+ list = (const cell_t *)prop->val.val;
+ if (!list)
+ return -ENOENT;
+ list_end = list + size / sizeof(*list);
+
+ /* Loop over the phandles until all the requested entry is found */
+ while (list < list_end) {
+ rc = -EINVAL;
+ count = 0;
+
+ /*
+ * If phandle is 0, then it is an empty entry with no
+ * arguments. Skip forward to the next entry.
+ */
+ phandle = fdt32_to_cpu(*list++);
+ if (phandle) {
+ /*
+ * Find the provider node and parse the #*-cells
+ * property to determine the argument length.
+ *
+ * This is not needed if the cell count is hard-coded
+ * (i.e. cells_name not set, but cell_count is set),
+ * except when we're going to return the found node
+ * below.
+ */
+ if (cells_name || cur_index == index) {
+ node = get_node_by_phandle(root, phandle);
+ if (!node) {
+ pr_err("could not find phandle %u\n",
+ phandle);
+ goto err;
+ }
+ }
+
+ if (cells_name) {
+ struct property *cells_prop;
+
+ cells_prop = get_property(node, cells_name);
+ if (!cells_prop) {
+ pr_err("could not get %s for %s\n",
+ cells_name,
+ node->fullpath);
+ goto err;
+ }
+
+ count = propval_cell(cells_prop);
+ } else {
+ count = cell_count;
+ }
+
+ /*
+ * Make sure that the arguments actually fit in the
+ * remaining property data length
+ */
+ if (list + count > list_end) {
+ pr_err("arguments longer than property\n");
+ goto err;
+ }
+ }
+
+ /*
+ * All of the error cases above bail out of the loop, so at
+ * this point, the parsing is successful. If the requested
+ * index matches, then fill the out_args structure and return,
+ * or return -ENOENT for an empty entry.
+ */
+ rc = -ENOENT;
+ if (cur_index == index) {
+ if (!phandle)
+ goto err;
+
+ if (out_args) {
+ int i;
+ if (count > MAX_PHANDLE_ARGS) {
+ pr_warn("argument count higher than MAX_PHANDLE_ARGS\n");
+ count = MAX_PHANDLE_ARGS;
+ }
+ out_args->np = node;
+ out_args->args_count = count;
+ for (i = 0; i < count; i++)
+ out_args->args[i] = fdt32_to_cpu(*list++);
+ }
+
+ /* Found it! return success */
+ return 0;
+ }
+
+ node = NULL;
+ list += count;
+ cur_index++;
+ }
+
+ /*
+ * Unlock node before returning result; will be one of:
+ * -ENOENT : index is for empty phandle
+ * -EINVAL : parsing error on data
+ * [1..n] : Number of phandle (count mode; when index = -1)
+ */
+ rc = index < 0 ? cur_index : -ENOENT;
+ err:
+ return rc;
+}
+
+struct node *propval_parse_phandle(struct node *root,
+ struct property *prop, int index)
+{
+ struct of_phandle_args args;
+
+ if (index < 0)
+ return NULL;
+
+ if (parse_phandle_with_args(root, prop, NULL, 0,
+ index, &args))
+ return NULL;
+
+ return args.np;
+}
+
+int propval_parse_phandle_with_args(struct node *root,
+ struct property *prop,
+ const char *cells_name, int index,
+ struct of_phandle_args *out_args)
+{
+ if (index < 0)
+ return -EINVAL;
+ return parse_phandle_with_args(root, prop, cells_name, 0,
+ index, out_args);
+}
+
+int propval_parse_phandle_with_fixed_args(struct node *root,
+ struct property *prop, int cell_count,
+ int index,
+ struct of_phandle_args *out_args)
+{
+ if (index < 0)
+ return -EINVAL;
+ return parse_phandle_with_args(root, prop, NULL, cell_count,
+ index, out_args);
+}
+
+int propval_count_phandle_with_args(struct node *root,
+ struct property *prop,
+ const char *cells_name)
+{
+ return parse_phandle_with_args(root, prop, cells_name, 0, -1,
+ NULL);
+}
+
struct property *get_property_by_label(struct node *tree, const char *label,
struct node **node)
{
@@ -456,6 +680,12 @@ struct node *get_node_by_path(struct node *tree, const char *path)
while (path[0] == '/')
path++;
+ if (!(*path)) {
+ if (tree->deleted)
+ return NULL;
+ return tree;
+ }
+
p = strchr(path, '/');
for_each_child(tree, child) {
--
1.8.5.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 2/5] dtc: livetree: Add more tree parsing helpers
2014-02-20 18:06 ` [PATCH RFC 2/5] dtc: livetree: Add more tree parsing helpers Tomasz Figa
@ 2014-03-09 12:03 ` David Gibson
0 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2014-03-09 12:03 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Feb 20, 2014 at 07:06:48PM +0100, Tomasz Figa wrote:
> This patch extends the set of parsing helpers available in dtc with
> string and phandle parsing.
>
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> ---
> dtc.h | 28 ++++++++
> livetree.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 258 insertions(+)
>
> diff --git a/dtc.h b/dtc.h
> index e95bed7..9ce9d12 100644
> --- a/dtc.h
> +++ b/dtc.h
> @@ -163,6 +163,13 @@ struct node {
> struct label *labels;
> };
>
> +#define MAX_PHANDLE_ARGS 16
> +struct of_phandle_args {
> + struct node *np;
> + int args_count;
> + uint32_t args[MAX_PHANDLE_ARGS];
> +};
> +
> #define for_each_label_withdel(l0, l) \
> for ((l) = (l0); (l); (l) = (l)->next)
>
> @@ -184,6 +191,11 @@ struct node {
> for_each_child_withdel(n, c) \
> if (!(c)->deleted)
>
> +#define for_each_propval_string(p, s) \
> + for (s = propval_next_string(p, NULL); \
> + s; \
> + s = propval_next_string(p, s))
> +
> void add_label(struct label **labels, char *label);
> void delete_labels(struct label **labels);
>
> @@ -208,6 +220,22 @@ void delete_node(struct node *node);
> const char *get_unitname(struct node *node);
> struct property *get_property(struct node *node, const char *propname);
> cell_t propval_cell(struct property *prop);
> +int propval_string_count(struct node *np, struct property *prop);
> +const char *propval_next_string(struct property *prop, const char *cur);
> +int propval_match_string(struct property *prop, const char *string);
> +struct node *propval_parse_phandle(struct node *root,
> + struct property *prop, int index);
> +int propval_parse_phandle_with_args(struct node *root,
> + struct property *prop,
> + const char *cells_name, int index,
> + struct of_phandle_args *out_args);
> +int propval_parse_phandle_with_fixed_args(struct node *root,
> + struct property *prop, int cell_count,
> + int index,
> + struct of_phandle_args *out_args);
> +int propval_count_phandle_with_args(struct node *root,
> + struct property *prop,
> + const char *cells_name);
> struct property *get_property_by_label(struct node *tree, const char *label,
> struct node **node);
> struct marker *get_marker_label(struct node *tree, const char *label,
> diff --git a/livetree.c b/livetree.c
> index b61465f..015ed06 100644
> --- a/livetree.c
> +++ b/livetree.c
> @@ -377,6 +377,230 @@ cell_t propval_cell(struct property *prop)
> return fdt32_to_cpu(*((cell_t *)prop->val.val));
> }
>
> +int propval_string_count(struct node *np, struct property *prop)
> +{
> + int i = 0;
> + size_t l = 0, total = 0;
> + const char *p;
> +
> + if (!prop)
> + return -EINVAL;
1) using Unix error codes as return values is a convention which is
not currently used anywhere in dtc. Introducing it in the middle of
other changes is not a good idea.
2) Surely calling this on a NULL property is a bug in the caller, so
it should be an assert().
> + if (!prop->val.val)
> + return -ENODATA;
An empty property is a valid stringlist with 0 entries, so this should
return 0 not an error.
> + if (strnlen(prop->val.val, prop->val.len) >= prop->val.len)
> + return -EILSEQ;
> +
> + p = prop->val.val;
> +
> + for (i = 0; total < prop->val.len; total += l, p += l, i++)
> + l = strlen(p) + 1;
> +
> + return i;
> +}
> +
> +const char *propval_next_string(struct property *prop, const char *cur)
> +{
> + const char *curv = cur;
> +
> + if (!prop)
> + return NULL;
> +
> + if (!cur)
> + return prop->val.val;
> +
> + curv += strlen(cur) + 1;
> + if (curv >= prop->val.val + prop->val.len)
> + return NULL;
> +
> + return curv;
> +}
> +
> +int propval_match_string(struct property *prop, const char *string)
> +{
> + size_t l;
> + int i;
> + const char *p, *end;
> +
> + if (!prop)
> + return -EINVAL;
> + if (!prop->val.val)
> + return -ENODATA;
> +
> + p = prop->val.val;
> + end = p + prop->val.len;
> +
> + for (i = 0; p < end; i++, p += l) {
> + l = strlen(p) + 1;
> + if (p + l > end)
> + return -EILSEQ;
> + if (strcmp(string, p) == 0)
> + return i; /* Found it; return index */
> + }
> + return -ENODATA;
> +}
> +
> +static int parse_phandle_with_args(struct node *root,
> + struct property *prop,
> + const char *cells_name,
> + int cell_count, int index,
> + struct of_phandle_args *out_args)
> +{
> + const cell_t *list, *list_end;
> + int rc = 0, size, cur_index = 0;
> + uint32_t count = 0;
> + struct node *node = NULL;
> + cell_t phandle;
> +
> + /* Retrieve the phandle list property */
> + size = prop->val.len;
> + list = (const cell_t *)prop->val.val;
> + if (!list)
> + return -ENOENT;
> + list_end = list + size / sizeof(*list);
> +
> + /* Loop over the phandles until all the requested entry is found */
> + while (list < list_end) {
> + rc = -EINVAL;
> + count = 0;
> +
> + /*
> + * If phandle is 0, then it is an empty entry with no
> + * arguments. Skip forward to the next entry.
> + */
> + phandle = fdt32_to_cpu(*list++);
> + if (phandle) {
Using continue would avoid extraneous indentation and make this easier
to read.
> + /*
> + * Find the provider node and parse the #*-cells
> + * property to determine the argument length.
> + *
> + * This is not needed if the cell count is hard-coded
> + * (i.e. cells_name not set, but cell_count is set),
> + * except when we're going to return the found node
> + * below.
> + */
> + if (cells_name || cur_index == index) {
> + node = get_node_by_phandle(root, phandle);
> + if (!node) {
> + pr_err("could not find phandle %u\n",
> + phandle);
> + goto err;
> + }
> + }
> +
> + if (cells_name) {
> + struct property *cells_prop;
> +
> + cells_prop = get_property(node, cells_name);
> + if (!cells_prop) {
> + pr_err("could not get %s for %s\n",
> + cells_name,
> + node->fullpath);
> + goto err;
> + }
> +
> + count = propval_cell(cells_prop);
> + } else {
> + count = cell_count;
> + }
> +
> + /*
> + * Make sure that the arguments actually fit in the
> + * remaining property data length
> + */
> + if (list + count > list_end) {
> + pr_err("arguments longer than property\n");
> + goto err;
> + }
> + }
> +
> + /*
> + * All of the error cases above bail out of the loop, so at
> + * this point, the parsing is successful. If the requested
> + * index matches, then fill the out_args structure and return,
> + * or return -ENOENT for an empty entry.
> + */
> + rc = -ENOENT;
> + if (cur_index == index) {
> + if (!phandle)
> + goto err;
> +
> + if (out_args) {
> + int i;
> + if (count > MAX_PHANDLE_ARGS) {
> + pr_warn("argument count higher than MAX_PHANDLE_ARGS\n");
> + count = MAX_PHANDLE_ARGS;
That's more than a warn. If you've run out of space here that pretty
much has to be a fatal error.
> + }
> + out_args->np = node;
> + out_args->args_count = count;
> + for (i = 0; i < count; i++)
> + out_args->args[i] = fdt32_to_cpu(*list++);
> + }
> +
> + /* Found it! return success */
> + return 0;
> + }
> +
> + node = NULL;
> + list += count;
> + cur_index++;
> + }
> +
> + /*
> + * Unlock node before returning result; will be one of:
Hrm... looks like a comment suffering from copy/paste without updating
for the new context.
> + * -ENOENT : index is for empty phandle
> + * -EINVAL : parsing error on data
> + * [1..n] : Number of phandle (count mode; when index = -1)
> + */
> + rc = index < 0 ? cur_index : -ENOENT;
> + err:
> + return rc;
> +}
> +
> +struct node *propval_parse_phandle(struct node *root,
> + struct property *prop, int index)
> +{
> + struct of_phandle_args args;
> +
> + if (index < 0)
> + return NULL;
Again, this is a bug in the caller; should be an assert().
> +
> + if (parse_phandle_with_args(root, prop, NULL, 0,
> + index, &args))
> + return NULL;
> +
> + return args.np;
> +}
> +
> +int propval_parse_phandle_with_args(struct node *root,
> + struct property *prop,
> + const char *cells_name, int index,
> + struct of_phandle_args *out_args)
> +{
> + if (index < 0)
> + return -EINVAL;
> + return parse_phandle_with_args(root, prop, cells_name, 0,
> + index, out_args);
> +}
> +
> +int propval_parse_phandle_with_fixed_args(struct node *root,
> + struct property *prop, int cell_count,
> + int index,
> + struct of_phandle_args *out_args)
> +{
> + if (index < 0)
> + return -EINVAL;
> + return parse_phandle_with_args(root, prop, NULL, cell_count,
> + index, out_args);
> +}
> +
> +int propval_count_phandle_with_args(struct node *root,
> + struct property *prop,
> + const char *cells_name)
> +{
> + return parse_phandle_with_args(root, prop, cells_name, 0, -1,
> + NULL);
> +}
> +
> struct property *get_property_by_label(struct node *tree, const char *label,
> struct node **node)
> {
> @@ -456,6 +680,12 @@ struct node *get_node_by_path(struct node *tree, const char *path)
> while (path[0] == '/')
> path++;
>
> + if (!(*path)) {
> + if (tree->deleted)
> + return NULL;
> + return tree;
> + }
This change needs a justification.
> p = strchr(path, '/');
>
> for_each_child(tree, child) {
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140309/089cd3f7/attachment-0001.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC 3/5] Implement DT schema checker using hybrid approach
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
2014-02-20 18:06 ` [PATCH RFC 1/5] dtc: Add helpers for various message levels Tomasz Figa
2014-02-20 18:06 ` [PATCH RFC 2/5] dtc: livetree: Add more tree parsing helpers Tomasz Figa
@ 2014-02-20 18:06 ` Tomasz Figa
2014-03-09 12:17 ` David Gibson
2014-02-20 18:06 ` [PATCH RFC 4/5] Add sample C-based generic bindings Tomasz Figa
` (3 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Tomasz Figa @ 2014-02-20 18:06 UTC (permalink / raw)
To: linux-arm-kernel
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 <swarren@wwwdotorg.org>
[further development into hybrid solution]
Signed-off-by: Tomasz Figa <t.figa@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 <dwg@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 <dwg@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 <swarren@wwwdotorg.org>
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Tomasz Figa <t.figa@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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 3/5] Implement DT schema checker using hybrid approach
2014-02-20 18:06 ` [PATCH RFC 3/5] Implement DT schema checker using hybrid approach Tomasz Figa
@ 2014-03-09 12:17 ` David Gibson
0 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2014-03-09 12:17 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Feb 20, 2014 at 07:06:49PM +0100, Tomasz Figa wrote:
> 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 <swarren@wwwdotorg.org>
> [further development into hybrid solution]
> Signed-off-by: Tomasz Figa <t.figa@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,
> };
Blech. The whole point of the checks infrastructure is to keep track
of checks for different possible problems in the tree. It has stuff
to handle possible interdependencies, and to keep checking what it
can, even if some checks have already failed.
And you throw that all away by handling all the schemas in a single
check. Instead, each schema should be loaded as a separate check.
This also makes the C/schema hybrid approach much more natural, as
there's no artificial distinction between a schema/check implemented
directly in C and one loaded from a script/schema parser.
> 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;
Really? Most of the boot_info structure makes no sense when parsing
schemas rather than an actual device tree. Even if you're trying to
use a common parser (which I think is a mistake), then at least
implement a different wrapper which uses an appropriate output type.
> 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
[snip]
> diff --git a/dtss-parser.y b/dtss-parser.y
> new file mode 100644
> index 0000000..1c807da
> --- /dev/null
> +++ b/dtss-parser.y
[snip]
My mistake, there is a new lexer and parser, so there's no excuse for
the duplicated boot_info structure above. Or for the tortured schema
syntax to make it look kind of like a device tree itself.
> 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 <swarren@wwwdotorg.org>
> + *
> + * Copyright (C) 2014 Samsung Electronics Co., Ltd.
> + * Tomasz Figa <t.figa@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;
> +};
AFAICT all the above is only used internally in schema.c so there's no
need for it to be in a header.
> +
> +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;
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140309/e265e3ec/attachment-0001.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC 4/5] Add sample C-based generic bindings
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
` (2 preceding siblings ...)
2014-02-20 18:06 ` [PATCH RFC 3/5] Implement DT schema checker using hybrid approach Tomasz Figa
@ 2014-02-20 18:06 ` Tomasz Figa
2014-02-20 18:06 ` [PATCH RFC 5/5] Add sample DTS and DTSS schema Tomasz Figa
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Tomasz Figa @ 2014-02-20 18:06 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
---
Makefile.dtc | 7 +-
schemas/clock/clock.c | 77 +++++
schemas/gpio/gpio.c | 93 ++++++
schemas/i2c/i2c.c | 42 +++
schemas/interrupt-controller/interrupts.c | 452 ++++++++++++++++++++++++++++++
schemas/mmio-bus.c | 97 +++++++
6 files changed, 767 insertions(+), 1 deletion(-)
create mode 100644 schemas/clock/clock.c
create mode 100644 schemas/gpio/gpio.c
create mode 100644 schemas/i2c/i2c.c
create mode 100644 schemas/interrupt-controller/interrupts.c
create mode 100644 schemas/mmio-bus.c
diff --git a/Makefile.dtc b/Makefile.dtc
index bf19564..b75da69 100644
--- a/Makefile.dtc
+++ b/Makefile.dtc
@@ -13,7 +13,12 @@ DTC_SRCS = \
srcpos.c \
treesource.c \
util.c \
- schemas/schema.c
+ schemas/mmio-bus.c \
+ schemas/schema.c \
+ schemas/clock/clock.c \
+ schemas/gpio/gpio.c \
+ schemas/i2c/i2c.c \
+ schemas/interrupt-controller/interrupts.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/schemas/clock/clock.c b/schemas/clock/clock.c
new file mode 100644
index 0000000..d22b759
--- /dev/null
+++ b/schemas/clock/clock.c
@@ -0,0 +1,77 @@
+#include "dtc.h"
+#include "schema.h"
+
+static void clocks_check_names(struct node *root, struct node *node,
+ struct property *names, bool optional)
+{
+ const char *name = NULL;
+ struct property *clocks, *clock_names;
+
+ clocks = require_property(node, "clocks");
+ clock_names = require_property(node, "clock-names");
+
+ if (!clocks || !clock_names)
+ return;
+
+ for_each_propval_string(names, name) {
+ struct of_phandle_args args;
+ int ret;
+
+ ret = propval_match_string(clock_names, name);
+ if (ret < 0) {
+ if (optional)
+ continue;
+
+ pr_err("clock '%s' not specified in node '%s'\n",
+ name, node->fullpath);
+ continue;
+ }
+
+ ret = propval_parse_phandle_with_args(root, clocks,
+ "#clock-cells", ret,
+ &args);
+ if (ret < 0)
+ pr_err("failed to parse specifier of clock '%s' in node '%s'\n",
+ name, node->fullpath);
+ }
+}
+
+static void clocks_check_count(struct node *root, struct node *node,
+ struct property *count_prop)
+{
+ struct property *clocks;
+ cell_t count = propval_cell(count_prop);
+ int ret;
+
+ clocks = require_property(node, "clocks");
+ if (!clocks)
+ return;
+
+ ret = propval_count_phandle_with_args(root, clocks, "#clock-cells");
+ if (ret < 0)
+ pr_err("failed to parse clocks property\n");
+ else if (ret < count)
+ pr_err("not enough clock specifiers (expected %u, got %d)\n",
+ count, ret);
+}
+
+static void generic_checkfn_clocks(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+
+ if (!params) {
+ pr_err("schema clocks requires arguments\n");
+ return;
+ }
+
+ prop = get_property(params, "names");
+ if (prop)
+ clocks_check_names(root, node, prop, !required);
+
+ prop = get_property(params, "count");
+ if (prop)
+ clocks_check_count(root, node, prop);
+}
+GENERIC_SCHEMA("clocks", clocks);
diff --git a/schemas/gpio/gpio.c b/schemas/gpio/gpio.c
new file mode 100644
index 0000000..9100c95
--- /dev/null
+++ b/schemas/gpio/gpio.c
@@ -0,0 +1,93 @@
+#include "schema.h"
+
+static void check_gpios_named(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required,
+ const char *prop_name)
+{
+ struct property *prop;
+ cell_t count;
+ int ret;
+ int i;
+
+ ret = schema_get_param_cell(params, "count", &count);
+ if (ret < 0) {
+ schema_err(schema, "missing schema argument: 'count'\n");
+ return;
+ }
+
+ if (required)
+ prop = require_property(node, prop_name);
+ else
+ prop = get_property(node, prop_name);
+ if (!prop)
+ return;
+
+ for (i = 0; i < count; ++i) {
+ struct of_phandle_args args;
+
+ ret = propval_parse_phandle_with_args(root, prop,
+ "#gpio-cells", i, &args);
+ if (ret < 0)
+ pr_err("failed to parse gpio specifier %d in '%s' property of node '%s'\n",
+ i, prop_name, node->fullpath);
+ }
+}
+
+static void generic_checkfn_gpios(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ check_gpios_named(schema, root, node, params, required, "gpios");
+}
+GENERIC_SCHEMA("gpios", gpios);
+
+static void generic_checkfn_named_gpios(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ const char *name;
+
+ prop = schema_get_param(params, "name");
+ if (!prop) {
+ schema_err(schema, "missing schema argument: 'name'\n");
+ return;
+ }
+
+ name = propval_next_string(prop, NULL);
+
+ check_gpios_named(schema, root, node, params, required, name);
+}
+GENERIC_SCHEMA("named-gpios", named_gpios);
+
+static void generic_checkfn_gpio_provider(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ cell_t cells, val;
+ int ret;
+
+ ret = schema_get_param_cell(params, "cells", &cells);
+ if (ret < 0) {
+ schema_err(schema, "missing schema argument: 'cells'\n");
+ return;
+ }
+
+ prop = require_property(node, "gpio-controller");
+ if (!prop)
+ return;
+
+ prop = require_property(node, "#gpio-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val != cells) {
+ pr_err("wrong value of #interrupt-cells property in node '%s' (expected %u, got %u)\n",
+ node->fullpath, cells, val);
+ return;
+ }
+}
+GENERIC_SCHEMA("gpio-provider", gpio_provider);
diff --git a/schemas/i2c/i2c.c b/schemas/i2c/i2c.c
new file mode 100644
index 0000000..ba8fd34
--- /dev/null
+++ b/schemas/i2c/i2c.c
@@ -0,0 +1,42 @@
+#include "schema.h"
+
+static void generic_checkfn_i2c_device(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ prop = require_property(node, "reg");
+ if (!prop)
+ return;
+
+ if (prop->val.len != sizeof(cell_t))
+ node_err(node, "i2c-bus expects reg property to be a single cell\n");
+
+ /* TODO: Check if parent device is an i2c bus. */
+}
+GENERIC_SCHEMA("i2c-device", i2c_device);
+
+static void generic_checkfn_i2c_bus(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ cell_t val;
+
+ prop = require_property(node, "#address-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val != 1)
+ node_err(node, "i2c-bus requires #address-cells == 1\n");
+
+ prop = require_property(node, "#size-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val != 0)
+ node_err(node, "i2c-bus requires #size-cells == 0\n");
+}
+GENERIC_SCHEMA("i2c-bus", i2c_bus);
diff --git a/schemas/interrupt-controller/interrupts.c b/schemas/interrupt-controller/interrupts.c
new file mode 100644
index 0000000..7eda441
--- /dev/null
+++ b/schemas/interrupt-controller/interrupts.c
@@ -0,0 +1,452 @@
+#include "schema.h"
+
+/*******************************************************************************
+ * Copypasta from kernel's drivers/of/irq.c starts here.
+ * (Well, maybe with some minor changes to make it compile here.)
+ ******************************************************************************/
+
+/* Adaptation glue... */
+
+#define pr_debug(...)
+typedef cell_t __be32;
+typedef cell_t phandle;
+#define of_node_get(node) (node)
+#define of_node_put(node)
+#define of_irq_workarounds (0)
+#define OF_IMAP_OLDWORLD_MAC 0x00000001
+#define OF_IMAP_NO_PHANDLE 0x00000002
+#define of_irq_dflt_pic (NULL)
+#define be32_to_cpu(val) fdt32_to_cpu((val))
+#define be32_to_cpup(ptr) be32_to_cpu(*(ptr))
+#define cpu_to_be32(val) cpu_to_fdt32(val)
+#define raw_spin_lock_irqsave(...)
+#define raw_spin_unlock_irqrestore(...)
+typedef uint32_t u32;
+#define WARN_ON(x) (x)
+#define of_irq_parse_oldworld(...) (-EINVAL)
+#define of_node_full_name(x) ((x)->fullpath)
+
+static const cell_t dummy = 0;
+
+static const void *of_get_property(struct node *np, const char *name,
+ int *lenp)
+{
+ struct property *prop;
+
+ prop = get_property(np, name);
+ if(!prop)
+ return NULL;
+
+ if (lenp)
+ *lenp = prop->val.len;
+
+ if (!prop->val.val)
+ return &dummy;
+
+ return prop->val.val;
+}
+
+static struct node *of_get_parent(const struct node *node)
+{
+ return node ? node->parent : NULL;
+}
+
+static int of_device_is_available(struct node *device)
+{
+ const char *status;
+ int statlen;
+
+ if (!device)
+ return 0;
+
+ status = of_get_property(device, "status", &statlen);
+ if (status == NULL)
+ return 1;
+
+ if (statlen > 0) {
+ if (!strcmp(status, "okay") || !strcmp(status, "ok"))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* End of adaptation glue. */
+
+/**
+ * of_irq_find_parent - Given a device node, find its interrupt parent node
+ * @child: pointer to device node
+ *
+ * Returns a pointer to the interrupt parent node, or NULL if the interrupt
+ * parent could not be determined.
+ */
+static struct node *of_irq_find_parent(struct node *root, struct node *child)
+{
+ struct node *p;
+ const __be32 *parp;
+
+ if (!of_node_get(child))
+ return NULL;
+
+ do {
+ parp = of_get_property(child, "interrupt-parent", NULL);
+ if (parp == NULL)
+ p = of_get_parent(child);
+ else {
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ p = of_node_get(of_irq_dflt_pic);
+ else
+ p = get_node_by_phandle(root,
+ be32_to_cpup(parp));
+ }
+ of_node_put(child);
+ child = p;
+ } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);
+
+ return p;
+}
+
+/**
+ * of_irq_parse_raw - Low level interrupt tree parsing
+ * @parent: the device interrupt parent
+ * @addr: address specifier (start of "reg" property of the device) in be32 format
+ * @out_irq: structure of_irq updated by this function
+ *
+ * Returns 0 on success and a negative number on error
+ *
+ * This function is a low-level interrupt tree walking function. It
+ * can be used to do a partial walk with synthetized reg and interrupts
+ * properties, for example when resolving PCI interrupts when no device
+ * node exist for the parent. It takes an interrupt specifier structure as
+ * input, walks the tree looking for any interrupt-map properties, translates
+ * the specifier for each map, and then returns the translated map.
+ */
+static int of_irq_parse_raw(struct node *root,
+ const __be32 *addr, struct of_phandle_args *out_irq)
+{
+ struct node *ipar, *tnode, *old = NULL, *newpar = NULL;
+ __be32 initial_match_array[MAX_PHANDLE_ARGS];
+ const __be32 *match_array = initial_match_array;
+ const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
+ u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
+ int imaplen, match, i;
+
+#ifdef DEBUG
+ of_print_phandle_args("of_irq_parse_raw: ", out_irq);
+#endif
+
+ ipar = of_node_get(out_irq->np);
+
+ /* First get the #interrupt-cells property of the current cursor
+ * that tells us how to interpret the passed-in intspec. If there
+ * is none, we are nice and just walk up the tree
+ */
+ do {
+ tmp = of_get_property(ipar, "#interrupt-cells", NULL);
+ if (tmp != NULL) {
+ intsize = be32_to_cpu(*tmp);
+ break;
+ }
+ tnode = ipar;
+ ipar = of_irq_find_parent(root, ipar);
+ of_node_put(tnode);
+ } while (ipar);
+ if (ipar == NULL) {
+ pr_debug(" -> no parent found !\n");
+ goto fail;
+ }
+
+ pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);
+
+ if (out_irq->args_count != intsize)
+ return -EINVAL;
+
+ /* Look for this #address-cells. We have to implement the old linux
+ * trick of looking for the parent here as some device-trees rely on it
+ */
+ old = of_node_get(ipar);
+ do {
+ tmp = of_get_property(old, "#address-cells", NULL);
+ tnode = of_get_parent(old);
+ of_node_put(old);
+ old = tnode;
+ } while (old && tmp == NULL);
+ of_node_put(old);
+ old = NULL;
+ addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);
+
+ pr_debug(" -> addrsize=%d\n", addrsize);
+
+ /* Range check so that the temporary buffer doesn't overflow */
+ if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS))
+ goto fail;
+
+ /* Precalculate the match array - this simplifies match loop */
+ for (i = 0; i < addrsize; i++)
+ initial_match_array[i] = addr ? addr[i] : 0;
+ for (i = 0; i < intsize; i++)
+ initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);
+
+ /* Now start the actual "proper" walk of the interrupt tree */
+ while (ipar != NULL) {
+ /* Now check if cursor is an interrupt-controller and if it is
+ * then we are done
+ */
+ if (of_get_property(ipar, "interrupt-controller", NULL) !=
+ NULL) {
+ pr_debug(" -> got it !\n");
+ return 0;
+ }
+
+ /*
+ * interrupt-map parsing does not work without a reg
+ * property when #address-cells != 0
+ */
+ if (addrsize && !addr) {
+ pr_debug(" -> no reg passed in when needed !\n");
+ goto fail;
+ }
+
+ /* Now look for an interrupt-map */
+ imap = of_get_property(ipar, "interrupt-map", &imaplen);
+ /* No interrupt map, check for an interrupt parent */
+ if (imap == NULL) {
+ pr_debug(" -> no map, getting parent\n");
+ newpar = of_irq_find_parent(root, ipar);
+ goto skiplevel;
+ }
+ imaplen /= sizeof(u32);
+
+ /* Look for a mask */
+ imask = of_get_property(ipar, "interrupt-map-mask", NULL);
+ if (!imask)
+ imask = dummy_imask;
+
+ /* Parse interrupt-map */
+ match = 0;
+ while (imaplen > (addrsize + intsize + 1) && !match) {
+ /* Compare specifiers */
+ match = 1;
+ for (i = 0; i < (addrsize + intsize); i++, imaplen--)
+ match &= !((match_array[i] ^ *imap++) & imask[i]);
+
+ pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
+
+ /* Get the interrupt parent */
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ newpar = of_node_get(of_irq_dflt_pic);
+ else
+ newpar = get_node_by_phandle(root,
+ be32_to_cpup(imap));
+ imap++;
+ --imaplen;
+
+ /* Check if not found */
+ if (newpar == NULL) {
+ pr_debug(" -> imap parent not found !\n");
+ goto fail;
+ }
+
+ if (!of_device_is_available(newpar))
+ match = 0;
+
+ /* Get #interrupt-cells and #address-cells of new
+ * parent
+ */
+ tmp = of_get_property(newpar, "#interrupt-cells", NULL);
+ if (tmp == NULL) {
+ pr_debug(" -> parent lacks #interrupt-cells!\n");
+ goto fail;
+ }
+ newintsize = be32_to_cpu(*tmp);
+ tmp = of_get_property(newpar, "#address-cells", NULL);
+ newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp);
+
+ pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
+ newintsize, newaddrsize);
+
+ /* Check for malformed properties */
+ if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS))
+ goto fail;
+ if (imaplen < (newaddrsize + newintsize))
+ goto fail;
+
+ imap += newaddrsize + newintsize;
+ imaplen -= newaddrsize + newintsize;
+
+ pr_debug(" -> imaplen=%d\n", imaplen);
+ }
+ if (!match)
+ goto fail;
+
+ /*
+ * Successfully parsed an interrrupt-map translation; copy new
+ * interrupt specifier into the out_irq structure
+ */
+ out_irq->np = newpar;
+
+ match_array = imap - newaddrsize - newintsize;
+ for (i = 0; i < newintsize; i++)
+ out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
+ out_irq->args_count = intsize = newintsize;
+ addrsize = newaddrsize;
+
+ skiplevel:
+ /* Iterate again with new parent */
+ pr_debug(" -> new parent: %s\n", of_node_full_name(newpar));
+ of_node_put(ipar);
+ ipar = newpar;
+ newpar = NULL;
+ }
+ fail:
+ of_node_put(ipar);
+ of_node_put(newpar);
+
+ return -EINVAL;
+}
+
+/**
+ * of_irq_parse_one - Resolve an interrupt for a device
+ * @device: the device whose interrupt is to be resolved
+ * @index: index of the interrupt to resolve
+ * @out_irq: structure of_irq filled by this function
+ *
+ * This function resolves an interrupt for a node by walking the interrupt tree,
+ * finding which interrupt controller node it is attached to, and returning the
+ * interrupt specifier that can be used to retrieve a Linux IRQ number.
+ */
+static int of_irq_parse_one(struct node *root, struct node *device,
+ int index, struct of_phandle_args *out_irq)
+{
+ struct node *p;
+ const __be32 *intspec, *tmp, *addr;
+ u32 intsize, intlen;
+ int i, res = -EINVAL;
+
+ pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
+
+ /* OldWorld mac stuff is "special", handle out of line */
+ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
+ return of_irq_parse_oldworld(device, index, out_irq);
+
+ /* Get the reg property (if any) */
+ addr = of_get_property(device, "reg", NULL);
+
+ /* Get the interrupts property */
+ intspec = of_get_property(device, "interrupts", (int *)&intlen);
+ if (intspec == NULL) {
+ struct property *prop;
+
+ prop = get_property(device, "interrupts-extended");
+ if (!prop)
+ return -EINVAL;
+
+ /* Try the new-style interrupts-extended */
+ res = propval_parse_phandle_with_args(root, prop,
+ "#interrupt-cells",
+ index, out_irq);
+ if (res)
+ return -EINVAL;
+ return of_irq_parse_raw(root, addr, out_irq);
+ }
+ intlen /= sizeof(*intspec);
+
+ pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
+
+ /* Look for the interrupt parent. */
+ p = of_irq_find_parent(root, device);
+ if (p == NULL)
+ return -EINVAL;
+
+ /* Get size of interrupt specifier */
+ tmp = of_get_property(p, "#interrupt-cells", NULL);
+ if (tmp == NULL)
+ goto out;
+ intsize = be32_to_cpu(*tmp);
+
+ pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
+
+ /* Check index */
+ if ((index + 1) * intsize > intlen)
+ goto out;
+
+ /* Copy intspec into irq structure */
+ intspec += index * intsize;
+ out_irq->np = p;
+ out_irq->args_count = intsize;
+ for (i = 0; i < intsize; i++)
+ out_irq->args[i] = be32_to_cpup(intspec++);
+
+ /* Check if there are any interrupt-map translations to process */
+ res = of_irq_parse_raw(root, addr, out_irq);
+ out:
+ of_node_put(p);
+ return res;
+}
+
+/*******************************************************************************
+ * Copypasta ends here.
+ ******************************************************************************/
+
+static void generic_checkfn_interrupts(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct of_phandle_args irq;
+ cell_t count;
+ int ret;
+ int i;
+
+ ret = schema_get_param_cell(params, "count", &count);
+ if (ret < 0) {
+ schema_err(schema, "missing schema argument: 'count'\n");
+ return;
+ }
+
+ if (!required
+ && !get_property(node, "interrupts")
+ && !get_property(node, "interrupts-extended"))
+ return;
+
+ for (i = 0; i < count; ++i) {
+ ret = of_irq_parse_one(root, node, i, &irq);
+ if (ret < 0)
+ pr_err("failed to parse interrupt entry %d of node '%s'\n",
+ i, node->fullpath);
+ }
+}
+GENERIC_SCHEMA("interrupts", interrupts);
+
+static void generic_checkfn_interrupt_controller(
+ const struct generic_schema *schema,
+ struct node *root,
+ struct node *node,
+ struct node *params,
+ bool required)
+{
+ struct property *prop;
+ cell_t cells, val;
+ int ret;
+
+ ret = schema_get_param_cell(params, "cells", &cells);
+ if (ret < 0) {
+ schema_err(schema, "missing schema argument: 'cells'\n");
+ return;
+ }
+
+ prop = require_property(node, "interrupt-controller");
+ if (!prop)
+ return;
+
+ prop = require_property(node, "#interrupt-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val != cells) {
+ pr_err("wrong value of #interrupt-cells property in node '%s' (expected %u, got %u)\n",
+ node->fullpath, cells, val);
+ return;
+ }
+}
+GENERIC_SCHEMA("interrupt-controller", interrupt_controller);
diff --git a/schemas/mmio-bus.c b/schemas/mmio-bus.c
new file mode 100644
index 0000000..bd7888d
--- /dev/null
+++ b/schemas/mmio-bus.c
@@ -0,0 +1,97 @@
+#include "schema.h"
+
+static unsigned int get_address_cells(struct node *node)
+{
+ struct property *prop;
+
+ prop = get_property(node->parent, "#address-cells");
+ if (!prop) {
+ pr_warn("missing #address-cells property, assuming 2\n");
+ return 2;
+ }
+
+ return propval_cell(prop);
+}
+
+static unsigned int get_size_cells(struct node *node)
+{
+ struct property *prop;
+
+ prop = get_property(node->parent, "#size-cells");
+ if (!prop) {
+ pr_warn("missing #size-cells property, assuming 1\n");
+ return 1;
+ }
+
+ return propval_cell(prop);
+}
+
+static void generic_checkfn_mmio_device(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ cell_t count;
+ unsigned int address_cells, size_cells;
+
+ if (!params) {
+ pr_err("schema mmio-device requires arguments\n");
+ return;
+ }
+
+ prop = get_property(params, "reg-count");
+ if (!prop) {
+ pr_err("schema mmio-device requires reg-count argument\n");
+ return;
+ }
+
+ count = propval_cell(prop);
+ if (!count) {
+ pr_err("wrong number of reg entries requested\n");
+ return;
+ }
+
+ if (!node->parent) {
+ pr_err("root node can not be an mmio-device\n");
+ return;
+ }
+
+ address_cells = get_address_cells(node);
+ size_cells = get_size_cells(node);
+
+ prop = require_property(node, "reg");
+ if (!prop)
+ return;
+
+ if (prop->val.len % (address_cells + size_cells))
+ pr_err("malformed reg property - not a multiple of (#address-cells + #size-cells)\n");
+
+ if (prop->val.len < count * (address_cells + size_cells))
+ pr_err("not enough entries in reg property - expected %u\n", count);
+}
+GENERIC_SCHEMA("mmio-device", mmio_device);
+
+static void generic_checkfn_mmio_bus(const struct generic_schema *schema,
+ struct node *root, struct node *node,
+ struct node *params, bool required)
+{
+ struct property *prop;
+ cell_t val;
+
+ prop = require_property(node, "#address-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val < 1)
+ pr_err("mmio-bus requires positive #address-cells value\n");
+
+ prop = require_property(node, "#size-cells");
+ if (!prop)
+ return;
+
+ val = propval_cell(prop);
+ if (val < 1)
+ pr_err("mmio-bus requires positive #size-cells value\n");
+}
+GENERIC_SCHEMA("mmio-bus", mmio_bus);
--
1.8.5.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 5/5] Add sample DTS and DTSS schema
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
` (3 preceding siblings ...)
2014-02-20 18:06 ` [PATCH RFC 4/5] Add sample C-based generic bindings Tomasz Figa
@ 2014-02-20 18:06 ` Tomasz Figa
2014-03-09 12:22 ` [PATCH RFC 0/5] Hybrid approach for DT schema checking David Gibson
2014-05-13 13:21 ` Tomasz Figa
6 siblings, 0 replies; 11+ messages in thread
From: Tomasz Figa @ 2014-02-20 18:06 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
---
sample.dts | 70 +++++++++++++++++++++++++++++++++++++++++++++++++
schema.dtss | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 156 insertions(+)
create mode 100644 sample.dts
create mode 100644 schema.dtss
diff --git a/sample.dts b/sample.dts
new file mode 100644
index 0000000..f962051
--- /dev/null
+++ b/sample.dts
@@ -0,0 +1,70 @@
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&gic>;
+ compatible = "foo,bar";
+ model = "Foo Bar board";
+
+ chosen {
+ bootargs = "console=ttySAC2,115200N8";
+ };
+
+ aliases {
+ i2c0 = &i2c_0;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x40000000 0x40000000>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gic: interrupt-controller at 10490000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ interrupt-controller;
+ reg = <0x10490000 0x1000>, <0x10480000 0x100>;
+ cpu-offset = <0x4000>;
+ };
+
+ i2c_0: i2c at 13860000 {
+ compatible = "nvidia,tegra20-i2c";
+ reg = <0x13860000 0x100>;
+ interrupts = <0 57 0>;
+ clocks = <&clocks 43>, <&clocks 28>;
+ clock-names = "div-clk", "fast-clk";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec at 33 {
+ compatible = "wlf,wm8903";
+ reg = <0x33>;
+ gpio-controller;
+ #gpio-cells = <1>;
+ };
+ };
+
+ gpio: pinctrl at 11400000 {
+ compatible = "foo,bar-gpio";
+ reg = <0x11400000 0x1000>;
+ interrupts = <0 47 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ clocks: clock-controller at 15440000 {
+ compatible = "foo,bar-clocks";
+ reg = <0x15440000 0x1000>;
+ #clock-cells = <1>;
+ };
+ };
+};
diff --git a/schema.dtss b/schema.dtss
new file mode 100644
index 0000000..7a819fe
--- /dev/null
+++ b/schema.dtss
@@ -0,0 +1,86 @@
+/*
+ * schema.dtss - Sample Device Tree schema file.
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Tomasz Figa <t.figa@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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dtss-v1/;
+
+root-node {
+ /match/ path = "/";
+ /require/ mmio-bus;
+
+ compatible;
+ /optional/ model;
+};
+
+wlf,wm8903 {
+ /match/ compatible = "wlf,wm8903";
+
+ /optional/ micdet-cfg;
+ /optional/ micdet-delay;
+ /optional/ gpio-cfg;
+
+ /require/ i2c-device;
+
+ /require/ gpio-provider {
+ cells = <1>;
+ };
+
+ /use/ interrupts {
+ count = <1>;
+ };
+};
+
+nvidia,tegra20-i2c {
+ /match/ compatible = "nvidia,tegra20-i2c", "nvidia,tegra30-i2c";
+
+ resets;
+ reset-names;
+ dmas;
+ dma-names;
+
+ /require/ i2c-bus;
+
+ /require/ mmio-device {
+ reg-count = <1>;
+ };
+
+ /require/ interrupts {
+ count = <1>;
+ };
+
+ /require/ clocks {
+ names = "div-clk", "fast-clk";
+ };
+};
+
+foo,bar-gpio {
+ /match/ compatible = "foo,bar-gpio";
+
+ /require/ interrupts {
+ count = <1>;
+ };
+
+ /require/ mmio-device {
+ reg-count = <1>;
+ };
+
+ /require/ gpio-provider {
+ cells = <2>;
+ };
+
+ /require/ interrupt-controller {
+ cells = <2>;
+ };
+};
--
1.8.5.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 0/5] Hybrid approach for DT schema checking
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
` (4 preceding siblings ...)
2014-02-20 18:06 ` [PATCH RFC 5/5] Add sample DTS and DTSS schema Tomasz Figa
@ 2014-03-09 12:22 ` David Gibson
2014-05-13 13:21 ` Tomasz Figa
6 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2014-03-09 12:22 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Feb 20, 2014 at 07:06:46PM +0100, Tomasz Figa wrote:
> This series adds a proof of concept framework to implement schema checker
> using a combined C and DTSS based approach. Several example bindings are
> also implemented using C and DTSS[1].
>
> Complex and generic bindings can be implemented directly in C and then
> instantiated from simple device-specific bindings using DTS-like DTSS
> language.
>
> A quick description of C part:
>
> A new check is registered in dtc checks framework to perform schema checking
> of each node. Checking is done by searching specified schema set for matching
> schemas (by compatible, device_type or absolute path) and applying matched
> schemas to the node.
We already have an infrastructure for handling C schemas - the checks
infrastructure itself. By all means extend/fix it where it needs it,
but implementing another layer for "schemas" as a special kind of
checks is silly.
[snip]
> A quick description of DTSS part:
>
> * DTSS is a DTS-like language for specification of simple bindings, e.g.
> bindings of particular devices. The basic syntax is very similar to DTS,
> with main elements being nodes and properties. At root level a series of
> nodes should be specified representing particular bindings:
>
> /dtss-v1/;
>
> binding1 {
> /* Definition of binding 1 */
> };
>
> binding2 {
> /* Definition of binding 2 */
> };
>
> * Matching key for each binding can be specified using /match/ keyword:
>
> root-node {
> /match/ path = "/";
> };
>
> wlf,wm8903 {
> /match/ compatible = "wlf,wm8903";
> };
>
> pci-bus {
> /match/ device_type = "memory";
> };
>
> Currently supported matches: path, compatible, device_type.
>
> * Bindings can be specified either by listing properties they require
> (or can use) directly or by instantiating generic C-based bindings.
>
> binding {
> required-property;
>
> /optional/ optional-property;
>
> /require/ required-generic-schema;
>
> /use/ optional-generic-schema {
> schema-argument = <1>;
> };
> };
>
> Generic schemas are implemented in C, as described above, and can use
> arguments specified in DTSS as properties. /require/ calls the schema with
> required=true, while /use/ with required=false.
>
> This is based on Stephen Warren's C based DT schema checker proof of
> concept patch adding C-based validation[2].
>
> TODO:
> - specification of subnodes directly from DTSS,
> - specification of simple property values from DTSS (cells, strings,
> phandles),
> - reporting of unrecognized properties,
> - probably many more...
>
> [1] Device Tree Schema Source
> [2] http://thread.gmane.org/gmane.linux.ports.arm.kernel/275896
>
> Tomasz Figa (5):
> dtc: Add helpers for various message levels
> dtc: livetree: Add more tree parsing helpers
> Another try of DT schema checker using hybrid C and DTSS based
> approach
> Add sample C-based generic bindings
> Add sample DTS and DTSS schema
>
> Makefile | 2 +-
> Makefile.dtc | 10 +-
> checks.c | 15 +
> dtc.c | 17 +-
> dtc.h | 57 ++++
> dtss-lexer.l | 291 +++++++++++++++++++
> dtss-parser.y | 341 ++++++++++++++++++++++
> livetree.c | 230 +++++++++++++++
> sample.dts | 70 +++++
> schema.dtss | 86 ++++++
> schemas/clock/clock.c | 77 +++++
> schemas/gpio/gpio.c | 93 ++++++
> schemas/i2c/i2c.c | 42 +++
> schemas/interrupt-controller/interrupts.c | 452 ++++++++++++++++++++++++++++++
> schemas/mmio-bus.c | 97 +++++++
> schemas/schema.c | 311 ++++++++++++++++++++
> schemas/schema.h | 89 ++++++
> srcpos.h | 2 +
> treesource.c | 22 ++
> 19 files changed, 2300 insertions(+), 4 deletions(-)
> create mode 100644 dtss-lexer.l
> create mode 100644 dtss-parser.y
> create mode 100644 sample.dts
> create mode 100644 schema.dtss
> create mode 100644 schemas/clock/clock.c
> create mode 100644 schemas/gpio/gpio.c
> create mode 100644 schemas/i2c/i2c.c
> create mode 100644 schemas/interrupt-controller/interrupts.c
> create mode 100644 schemas/mmio-bus.c
> create mode 100644 schemas/schema.c
> create mode 100644 schemas/schema.h
>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140309/0ab013b8/attachment-0001.sig>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC 0/5] Hybrid approach for DT schema checking
2014-02-20 18:06 [PATCH RFC 0/5] Hybrid approach for DT schema checking Tomasz Figa
` (5 preceding siblings ...)
2014-03-09 12:22 ` [PATCH RFC 0/5] Hybrid approach for DT schema checking David Gibson
@ 2014-05-13 13:21 ` Tomasz Figa
6 siblings, 0 replies; 11+ messages in thread
From: Tomasz Figa @ 2014-05-13 13:21 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Just wanted to add that you can also find the code in my dtc tree on
kernel.org:
git://git.kernel.org/pub/scm/linux/kernel/git/tfiga/dtc.git
or if you prefer web interface to quickly look through the changes:
https://git.kernel.org/cgit/linux/kernel/git/tfiga/dtc.git/
Best regards,
Tomasz
On 20.02.2014 19:06, Tomasz Figa wrote:
> This series adds a proof of concept framework to implement schema checker
> using a combined C and DTSS based approach. Several example bindings are
> also implemented using C and DTSS[1].
>
> Complex and generic bindings can be implemented directly in C and then
> instantiated from simple device-specific bindings using DTS-like DTSS
> language.
>
> A quick description of C part:
>
> A new check is registered in dtc checks framework to perform schema checking
> of each node. Checking is done by searching specified schema set for matching
> schemas (by compatible, device_type or absolute path) and applying matched
> schemas to the node.
>
> Schemas for complex generic bindings (such as interrupts, gpios, clocks, i2c,
> mmio-bus, etc.) can be implemented directly in C and instantiated from DTSS
> schemas of particular devices. An example C schema may look like:
>
> static void generic_checkfn_xxx_yyy(const struct generic_schema *schema,
> struct node *root, struct node *node,
> struct node *params, bool required)
> {
> /*
> * Get necessary schema arguments from DTSS schema by looking
> * at properties of @params node.
> *
> * Check whether @node node matches the schema.
> *
> * The @required argument may be used to check whether some error
> * conditions should be ignored (e.g. unspecified interrupt).
> */
> }
> GENERIC_SCHEMA("xxx-yyy", xxx_yyy);
>
> A quick description of DTSS part:
>
> * DTSS is a DTS-like language for specification of simple bindings, e.g.
> bindings of particular devices. The basic syntax is very similar to DTS,
> with main elements being nodes and properties. At root level a series of
> nodes should be specified representing particular bindings:
>
> /dtss-v1/;
>
> binding1 {
> /* Definition of binding 1 */
> };
>
> binding2 {
> /* Definition of binding 2 */
> };
>
> * Matching key for each binding can be specified using /match/ keyword:
>
> root-node {
> /match/ path = "/";
> };
>
> wlf,wm8903 {
> /match/ compatible = "wlf,wm8903";
> };
>
> pci-bus {
> /match/ device_type = "memory";
> };
>
> Currently supported matches: path, compatible, device_type.
>
> * Bindings can be specified either by listing properties they require
> (or can use) directly or by instantiating generic C-based bindings.
>
> binding {
> required-property;
>
> /optional/ optional-property;
>
> /require/ required-generic-schema;
>
> /use/ optional-generic-schema {
> schema-argument = <1>;
> };
> };
>
> Generic schemas are implemented in C, as described above, and can use
> arguments specified in DTSS as properties. /require/ calls the schema with
> required=true, while /use/ with required=false.
>
> This is based on Stephen Warren's C based DT schema checker proof of
> concept patch adding C-based validation[2].
>
> TODO:
> - specification of subnodes directly from DTSS,
> - specification of simple property values from DTSS (cells, strings,
> phandles),
> - reporting of unrecognized properties,
> - probably many more...
>
> [1] Device Tree Schema Source
> [2] http://thread.gmane.org/gmane.linux.ports.arm.kernel/275896
>
> Tomasz Figa (5):
> dtc: Add helpers for various message levels
> dtc: livetree: Add more tree parsing helpers
> Another try of DT schema checker using hybrid C and DTSS based
> approach
> Add sample C-based generic bindings
> Add sample DTS and DTSS schema
>
> Makefile | 2 +-
> Makefile.dtc | 10 +-
> checks.c | 15 +
> dtc.c | 17 +-
> dtc.h | 57 ++++
> dtss-lexer.l | 291 +++++++++++++++++++
> dtss-parser.y | 341 ++++++++++++++++++++++
> livetree.c | 230 +++++++++++++++
> sample.dts | 70 +++++
> schema.dtss | 86 ++++++
> schemas/clock/clock.c | 77 +++++
> schemas/gpio/gpio.c | 93 ++++++
> schemas/i2c/i2c.c | 42 +++
> schemas/interrupt-controller/interrupts.c | 452 ++++++++++++++++++++++++++++++
> schemas/mmio-bus.c | 97 +++++++
> schemas/schema.c | 311 ++++++++++++++++++++
> schemas/schema.h | 89 ++++++
> srcpos.h | 2 +
> treesource.c | 22 ++
> 19 files changed, 2300 insertions(+), 4 deletions(-)
> create mode 100644 dtss-lexer.l
> create mode 100644 dtss-parser.y
> create mode 100644 sample.dts
> create mode 100644 schema.dtss
> create mode 100644 schemas/clock/clock.c
> create mode 100644 schemas/gpio/gpio.c
> create mode 100644 schemas/i2c/i2c.c
> create mode 100644 schemas/interrupt-controller/interrupts.c
> create mode 100644 schemas/mmio-bus.c
> create mode 100644 schemas/schema.c
> create mode 100644 schemas/schema.h
>
^ permalink raw reply [flat|nested] 11+ messages in thread