drm/i915: Use single set of AUX powerwell ops for gen11+
[linux-2.6-microblaze.git] / lib / bootconfig.c
index afb2e76..912ef49 100644 (file)
@@ -6,12 +6,13 @@
 
 #define pr_fmt(fmt)    "bootconfig: " fmt
 
+#include <linux/bootconfig.h>
 #include <linux/bug.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/memblock.h>
 #include <linux/printk.h>
-#include <linux/bootconfig.h>
 #include <linux/string.h>
 
 /*
  * node (for array).
  */
 
-static struct xbc_node xbc_nodes[XBC_NODE_MAX] __initdata;
+static struct xbc_node *xbc_nodes __initdata;
 static int xbc_node_num __initdata;
 static char *xbc_data __initdata;
 static size_t xbc_data_size __initdata;
 static struct xbc_node *last_parent __initdata;
+static const char *xbc_err_msg __initdata;
+static int xbc_err_pos __initdata;
 
 static int __init xbc_parse_error(const char *msg, const char *p)
 {
-       int pos = p - xbc_data;
+       xbc_err_msg = msg;
+       xbc_err_pos = (int)(p - xbc_data);
 
-       pr_err("Parse error at pos %d: %s\n", pos, msg);
        return -EINVAL;
 }
 
@@ -532,7 +535,7 @@ struct xbc_node *find_match_node(struct xbc_node *node, char *k)
 
 static int __init __xbc_add_key(char *k)
 {
-       struct xbc_node *node;
+       struct xbc_node *node, *child;
 
        if (!xbc_valid_keyword(k))
                return xbc_parse_error("Invalid keyword", k);
@@ -542,8 +545,12 @@ static int __init __xbc_add_key(char *k)
 
        if (!last_parent)       /* the first level */
                node = find_match_node(xbc_nodes, k);
-       else
-               node = find_match_node(xbc_node_get_child(last_parent), k);
+       else {
+               child = xbc_node_get_child(last_parent);
+               if (child && xbc_node_is_value(child))
+                       return xbc_parse_error("Subkey is mixed with value", k);
+               node = find_match_node(child, k);
+       }
 
        if (node)
                last_parent = node;
@@ -573,10 +580,10 @@ static int __init __xbc_parse_keys(char *k)
        return __xbc_add_key(k);
 }
 
-static int __init xbc_parse_kv(char **k, char *v)
+static int __init xbc_parse_kv(char **k, char *v, int op)
 {
        struct xbc_node *prev_parent = last_parent;
-       struct xbc_node *node;
+       struct xbc_node *child;
        char *next;
        int c, ret;
 
@@ -584,12 +591,19 @@ static int __init xbc_parse_kv(char **k, char *v)
        if (ret)
                return ret;
 
+       child = xbc_node_get_child(last_parent);
+       if (child) {
+               if (xbc_node_is_key(child))
+                       return xbc_parse_error("Value is mixed with subkey", v);
+               else if (op == '=')
+                       return xbc_parse_error("Value is redefined", v);
+       }
+
        c = __xbc_parse_value(&v, &next);
        if (c < 0)
                return c;
 
-       node = xbc_add_sibling(v, XBC_VALUE);
-       if (!node)
+       if (!xbc_add_sibling(v, XBC_VALUE))
                return -ENOMEM;
 
        if (c == ',') { /* Array */
@@ -719,42 +733,61 @@ void __init xbc_destroy_all(void)
        xbc_data = NULL;
        xbc_data_size = 0;
        xbc_node_num = 0;
-       memset(xbc_nodes, 0, sizeof(xbc_nodes));
+       memblock_free(__pa(xbc_nodes), sizeof(struct xbc_node) * XBC_NODE_MAX);
+       xbc_nodes = NULL;
 }
 
 /**
  * xbc_init() - Parse given XBC file and build XBC internal tree
  * @buf: boot config text
+ * @emsg: A pointer of const char * to store the error message
+ * @epos: A pointer of int to store the error position
  *
  * This parses the boot config text in @buf. @buf must be a
  * null terminated string and smaller than XBC_DATA_MAX.
  * Return the number of stored nodes (>0) if succeeded, or -errno
  * if there is any error.
+ * In error cases, @emsg will be updated with an error message and
+ * @epos will be updated with the error position which is the byte offset
+ * of @buf. If the error is not a parser error, @epos will be -1.
  */
-int __init xbc_init(char *buf)
+int __init xbc_init(char *buf, const char **emsg, int *epos)
 {
        char *p, *q;
        int ret, c;
 
+       if (epos)
+               *epos = -1;
+
        if (xbc_data) {
-               pr_err("Error: bootconfig is already initialized.\n");
+               if (emsg)
+                       *emsg = "Bootconfig is already initialized";
                return -EBUSY;
        }
 
        ret = strlen(buf);
        if (ret > XBC_DATA_MAX - 1 || ret == 0) {
-               pr_err("Error: Config data is %s.\n",
-                       ret ? "too big" : "empty");
+               if (emsg)
+                       *emsg = ret ? "Config data is too big" :
+                               "Config data is empty";
                return -ERANGE;
        }
 
+       xbc_nodes = memblock_alloc(sizeof(struct xbc_node) * XBC_NODE_MAX,
+                                  SMP_CACHE_BYTES);
+       if (!xbc_nodes) {
+               if (emsg)
+                       *emsg = "Failed to allocate bootconfig nodes";
+               return -ENOMEM;
+       }
+       memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX);
        xbc_data = buf;
        xbc_data_size = ret + 1;
        last_parent = NULL;
 
        p = buf;
        do {
-               q = strpbrk(p, "{}=;\n#");
+               q = strpbrk(p, "{}=+;\n#");
                if (!q) {
                        p = skip_spaces(p);
                        if (*p != '\0')
@@ -765,8 +798,15 @@ int __init xbc_init(char *buf)
                c = *q;
                *q++ = '\0';
                switch (c) {
+               case '+':
+                       if (*q++ != '=') {
+                               ret = xbc_parse_error("Wrong '+' operator",
+                                                       q - 2);
+                               break;
+                       }
+                       /* Fall through */
                case '=':
-                       ret = xbc_parse_kv(&p, q);
+                       ret = xbc_parse_kv(&p, q, c);
                        break;
                case '{':
                        ret = xbc_open_brace(&p, q);
@@ -787,9 +827,13 @@ int __init xbc_init(char *buf)
        if (!ret)
                ret = xbc_verify_tree();
 
-       if (ret < 0)
+       if (ret < 0) {
+               if (epos)
+                       *epos = xbc_err_pos;
+               if (emsg)
+                       *emsg = xbc_err_msg;
                xbc_destroy_all();
-       else
+       else
                ret = xbc_node_num;
 
        return ret;