162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Extra Boot Config 462306a36Sopenharmony_ci * Masami Hiramatsu <mhiramat@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#ifdef __KERNEL__ 862306a36Sopenharmony_ci#include <linux/bootconfig.h> 962306a36Sopenharmony_ci#include <linux/bug.h> 1062306a36Sopenharmony_ci#include <linux/ctype.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/memblock.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#ifdef CONFIG_BOOT_CONFIG_EMBED 1762306a36Sopenharmony_ci/* embedded_bootconfig_data is defined in bootconfig-data.S */ 1862306a36Sopenharmony_ciextern __visible const char embedded_bootconfig_data[]; 1962306a36Sopenharmony_ciextern __visible const char embedded_bootconfig_data_end[]; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciconst char * __init xbc_get_embedded_bootconfig(size_t *size) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci *size = embedded_bootconfig_data_end - embedded_bootconfig_data; 2462306a36Sopenharmony_ci return (*size) ? embedded_bootconfig_data : NULL; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#else /* !__KERNEL__ */ 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * NOTE: This is only for tools/bootconfig, because tools/bootconfig will 3162306a36Sopenharmony_ci * run the parser sanity test. 3262306a36Sopenharmony_ci * This does NOT mean lib/bootconfig.c is available in the user space. 3362306a36Sopenharmony_ci * However, if you change this file, please make sure the tools/bootconfig 3462306a36Sopenharmony_ci * has no issue on building and running. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#include <linux/bootconfig.h> 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Extra Boot Config (XBC) is given as tree-structured ascii text of 4162306a36Sopenharmony_ci * key-value pairs on memory. 4262306a36Sopenharmony_ci * xbc_parse() parses the text to build a simple tree. Each tree node is 4362306a36Sopenharmony_ci * simply a key word or a value. A key node may have a next key node or/and 4462306a36Sopenharmony_ci * a child node (both key and value). A value node may have a next value 4562306a36Sopenharmony_ci * node (for array). 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct xbc_node *xbc_nodes __initdata; 4962306a36Sopenharmony_cistatic int xbc_node_num __initdata; 5062306a36Sopenharmony_cistatic char *xbc_data __initdata; 5162306a36Sopenharmony_cistatic size_t xbc_data_size __initdata; 5262306a36Sopenharmony_cistatic struct xbc_node *last_parent __initdata; 5362306a36Sopenharmony_cistatic const char *xbc_err_msg __initdata; 5462306a36Sopenharmony_cistatic int xbc_err_pos __initdata; 5562306a36Sopenharmony_cistatic int open_brace[XBC_DEPTH_MAX] __initdata; 5662306a36Sopenharmony_cistatic int brace_index __initdata; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#ifdef __KERNEL__ 5962306a36Sopenharmony_cistatic inline void * __init xbc_alloc_mem(size_t size) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return memblock_alloc(size, SMP_CACHE_BYTES); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline void __init xbc_free_mem(void *addr, size_t size) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci memblock_free(addr, size); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#else /* !__KERNEL__ */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline void *xbc_alloc_mem(size_t size) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return malloc(size); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void xbc_free_mem(void *addr, size_t size) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci free(addr); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci#endif 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * xbc_get_info() - Get the information of loaded boot config 8362306a36Sopenharmony_ci * @node_size: A pointer to store the number of nodes. 8462306a36Sopenharmony_ci * @data_size: A pointer to store the size of bootconfig data. 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Get the number of used nodes in @node_size if it is not NULL, 8762306a36Sopenharmony_ci * and the size of bootconfig data in @data_size if it is not NULL. 8862306a36Sopenharmony_ci * Return 0 if the boot config is initialized, or return -ENODEV. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ciint __init xbc_get_info(int *node_size, size_t *data_size) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci if (!xbc_data) 9362306a36Sopenharmony_ci return -ENODEV; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (node_size) 9662306a36Sopenharmony_ci *node_size = xbc_node_num; 9762306a36Sopenharmony_ci if (data_size) 9862306a36Sopenharmony_ci *data_size = xbc_data_size; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int __init xbc_parse_error(const char *msg, const char *p) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci xbc_err_msg = msg; 10562306a36Sopenharmony_ci xbc_err_pos = (int)(p - xbc_data); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * xbc_root_node() - Get the root node of extended boot config 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Return the address of root node of extended boot config. If the 11462306a36Sopenharmony_ci * extended boot config is not initiized, return NULL. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistruct xbc_node * __init xbc_root_node(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci if (unlikely(!xbc_data)) 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return xbc_nodes; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * xbc_node_index() - Get the index of XBC node 12662306a36Sopenharmony_ci * @node: A target node of getting index. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * Return the index number of @node in XBC node list. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ciint __init xbc_node_index(struct xbc_node *node) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return node - &xbc_nodes[0]; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * xbc_node_get_parent() - Get the parent XBC node 13762306a36Sopenharmony_ci * @node: An XBC node. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Return the parent node of @node. If the node is top node of the tree, 14062306a36Sopenharmony_ci * return NULL. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistruct xbc_node * __init xbc_node_get_parent(struct xbc_node *node) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return node->parent == XBC_NODE_MAX ? NULL : &xbc_nodes[node->parent]; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * xbc_node_get_child() - Get the child XBC node 14962306a36Sopenharmony_ci * @node: An XBC node. 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * Return the first child node of @node. If the node has no child, return 15262306a36Sopenharmony_ci * NULL. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistruct xbc_node * __init xbc_node_get_child(struct xbc_node *node) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return node->child ? &xbc_nodes[node->child] : NULL; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * xbc_node_get_next() - Get the next sibling XBC node 16162306a36Sopenharmony_ci * @node: An XBC node. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Return the NEXT sibling node of @node. If the node has no next sibling, 16462306a36Sopenharmony_ci * return NULL. Note that even if this returns NULL, it doesn't mean @node 16562306a36Sopenharmony_ci * has no siblings. (You also has to check whether the parent's child node 16662306a36Sopenharmony_ci * is @node or not.) 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistruct xbc_node * __init xbc_node_get_next(struct xbc_node *node) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return node->next ? &xbc_nodes[node->next] : NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * xbc_node_get_data() - Get the data of XBC node 17562306a36Sopenharmony_ci * @node: An XBC node. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Return the data (which is always a null terminated string) of @node. 17862306a36Sopenharmony_ci * If the node has invalid data, warn and return NULL. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ciconst char * __init xbc_node_get_data(struct xbc_node *node) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int offset = node->data & ~XBC_VALUE; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (WARN_ON(offset >= xbc_data_size)) 18562306a36Sopenharmony_ci return NULL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return xbc_data + offset; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic bool __init 19162306a36Sopenharmony_cixbc_node_match_prefix(struct xbc_node *node, const char **prefix) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci const char *p = xbc_node_get_data(node); 19462306a36Sopenharmony_ci int len = strlen(p); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (strncmp(*prefix, p, len)) 19762306a36Sopenharmony_ci return false; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci p = *prefix + len; 20062306a36Sopenharmony_ci if (*p == '.') 20162306a36Sopenharmony_ci p++; 20262306a36Sopenharmony_ci else if (*p != '\0') 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci *prefix = p; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return true; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/** 21062306a36Sopenharmony_ci * xbc_node_find_subkey() - Find a subkey node which matches given key 21162306a36Sopenharmony_ci * @parent: An XBC node. 21262306a36Sopenharmony_ci * @key: A key string. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Search a key node under @parent which matches @key. The @key can contain 21562306a36Sopenharmony_ci * several words jointed with '.'. If @parent is NULL, this searches the 21662306a36Sopenharmony_ci * node from whole tree. Return NULL if no node is matched. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistruct xbc_node * __init 21962306a36Sopenharmony_cixbc_node_find_subkey(struct xbc_node *parent, const char *key) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct xbc_node *node; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (parent) 22462306a36Sopenharmony_ci node = xbc_node_get_subkey(parent); 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci node = xbc_root_node(); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci while (node && xbc_node_is_key(node)) { 22962306a36Sopenharmony_ci if (!xbc_node_match_prefix(node, &key)) 23062306a36Sopenharmony_ci node = xbc_node_get_next(node); 23162306a36Sopenharmony_ci else if (*key != '\0') 23262306a36Sopenharmony_ci node = xbc_node_get_subkey(node); 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return node; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/** 24162306a36Sopenharmony_ci * xbc_node_find_value() - Find a value node which matches given key 24262306a36Sopenharmony_ci * @parent: An XBC node. 24362306a36Sopenharmony_ci * @key: A key string. 24462306a36Sopenharmony_ci * @vnode: A container pointer of found XBC node. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Search a value node under @parent whose (parent) key node matches @key, 24762306a36Sopenharmony_ci * store it in *@vnode, and returns the value string. 24862306a36Sopenharmony_ci * The @key can contain several words jointed with '.'. If @parent is NULL, 24962306a36Sopenharmony_ci * this searches the node from whole tree. Return the value string if a 25062306a36Sopenharmony_ci * matched key found, return NULL if no node is matched. 25162306a36Sopenharmony_ci * Note that this returns 0-length string and stores NULL in *@vnode if the 25262306a36Sopenharmony_ci * key has no value. And also it will return the value of the first entry if 25362306a36Sopenharmony_ci * the value is an array. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ciconst char * __init 25662306a36Sopenharmony_cixbc_node_find_value(struct xbc_node *parent, const char *key, 25762306a36Sopenharmony_ci struct xbc_node **vnode) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct xbc_node *node = xbc_node_find_subkey(parent, key); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!node || !xbc_node_is_key(node)) 26262306a36Sopenharmony_ci return NULL; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci node = xbc_node_get_child(node); 26562306a36Sopenharmony_ci if (node && !xbc_node_is_value(node)) 26662306a36Sopenharmony_ci return NULL; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (vnode) 26962306a36Sopenharmony_ci *vnode = node; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return node ? xbc_node_get_data(node) : ""; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * xbc_node_compose_key_after() - Compose partial key string of the XBC node 27662306a36Sopenharmony_ci * @root: Root XBC node 27762306a36Sopenharmony_ci * @node: Target XBC node. 27862306a36Sopenharmony_ci * @buf: A buffer to store the key. 27962306a36Sopenharmony_ci * @size: The size of the @buf. 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Compose the partial key of the @node into @buf, which is starting right 28262306a36Sopenharmony_ci * after @root (@root is not included.) If @root is NULL, this returns full 28362306a36Sopenharmony_ci * key words of @node. 28462306a36Sopenharmony_ci * Returns the total length of the key stored in @buf. Returns -EINVAL 28562306a36Sopenharmony_ci * if @node is NULL or @root is not the ancestor of @node or @root is @node, 28662306a36Sopenharmony_ci * or returns -ERANGE if the key depth is deeper than max depth. 28762306a36Sopenharmony_ci * This is expected to be used with xbc_find_node() to list up all (child) 28862306a36Sopenharmony_ci * keys under given key. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ciint __init xbc_node_compose_key_after(struct xbc_node *root, 29162306a36Sopenharmony_ci struct xbc_node *node, 29262306a36Sopenharmony_ci char *buf, size_t size) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci uint16_t keys[XBC_DEPTH_MAX]; 29562306a36Sopenharmony_ci int depth = 0, ret = 0, total = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!node || node == root) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (xbc_node_is_value(node)) 30162306a36Sopenharmony_ci node = xbc_node_get_parent(node); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci while (node && node != root) { 30462306a36Sopenharmony_ci keys[depth++] = xbc_node_index(node); 30562306a36Sopenharmony_ci if (depth == XBC_DEPTH_MAX) 30662306a36Sopenharmony_ci return -ERANGE; 30762306a36Sopenharmony_ci node = xbc_node_get_parent(node); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci if (!node && root) 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci while (--depth >= 0) { 31362306a36Sopenharmony_ci node = xbc_nodes + keys[depth]; 31462306a36Sopenharmony_ci ret = snprintf(buf, size, "%s%s", xbc_node_get_data(node), 31562306a36Sopenharmony_ci depth ? "." : ""); 31662306a36Sopenharmony_ci if (ret < 0) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci if (ret > size) { 31962306a36Sopenharmony_ci size = 0; 32062306a36Sopenharmony_ci } else { 32162306a36Sopenharmony_ci size -= ret; 32262306a36Sopenharmony_ci buf += ret; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci total += ret; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return total; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/** 33162306a36Sopenharmony_ci * xbc_node_find_next_leaf() - Find the next leaf node under given node 33262306a36Sopenharmony_ci * @root: An XBC root node 33362306a36Sopenharmony_ci * @node: An XBC node which starts from. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Search the next leaf node (which means the terminal key node) of @node 33662306a36Sopenharmony_ci * under @root node (including @root node itself). 33762306a36Sopenharmony_ci * Return the next node or NULL if next leaf node is not found. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cistruct xbc_node * __init xbc_node_find_next_leaf(struct xbc_node *root, 34062306a36Sopenharmony_ci struct xbc_node *node) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct xbc_node *next; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (unlikely(!xbc_data)) 34562306a36Sopenharmony_ci return NULL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!node) { /* First try */ 34862306a36Sopenharmony_ci node = root; 34962306a36Sopenharmony_ci if (!node) 35062306a36Sopenharmony_ci node = xbc_nodes; 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci /* Leaf node may have a subkey */ 35362306a36Sopenharmony_ci next = xbc_node_get_subkey(node); 35462306a36Sopenharmony_ci if (next) { 35562306a36Sopenharmony_ci node = next; 35662306a36Sopenharmony_ci goto found; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (node == root) /* @root was a leaf, no child node. */ 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci while (!node->next) { 36362306a36Sopenharmony_ci node = xbc_node_get_parent(node); 36462306a36Sopenharmony_ci if (node == root) 36562306a36Sopenharmony_ci return NULL; 36662306a36Sopenharmony_ci /* User passed a node which is not uder parent */ 36762306a36Sopenharmony_ci if (WARN_ON(!node)) 36862306a36Sopenharmony_ci return NULL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci node = xbc_node_get_next(node); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cifound: 37462306a36Sopenharmony_ci while (node && !xbc_node_is_leaf(node)) 37562306a36Sopenharmony_ci node = xbc_node_get_child(node); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return node; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/** 38162306a36Sopenharmony_ci * xbc_node_find_next_key_value() - Find the next key-value pair nodes 38262306a36Sopenharmony_ci * @root: An XBC root node 38362306a36Sopenharmony_ci * @leaf: A container pointer of XBC node which starts from. 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * Search the next leaf node (which means the terminal key node) of *@leaf 38662306a36Sopenharmony_ci * under @root node. Returns the value and update *@leaf if next leaf node 38762306a36Sopenharmony_ci * is found, or NULL if no next leaf node is found. 38862306a36Sopenharmony_ci * Note that this returns 0-length string if the key has no value, or 38962306a36Sopenharmony_ci * the value of the first entry if the value is an array. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ciconst char * __init xbc_node_find_next_key_value(struct xbc_node *root, 39262306a36Sopenharmony_ci struct xbc_node **leaf) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci /* tip must be passed */ 39562306a36Sopenharmony_ci if (WARN_ON(!leaf)) 39662306a36Sopenharmony_ci return NULL; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci *leaf = xbc_node_find_next_leaf(root, *leaf); 39962306a36Sopenharmony_ci if (!*leaf) 40062306a36Sopenharmony_ci return NULL; 40162306a36Sopenharmony_ci if ((*leaf)->child) 40262306a36Sopenharmony_ci return xbc_node_get_data(xbc_node_get_child(*leaf)); 40362306a36Sopenharmony_ci else 40462306a36Sopenharmony_ci return ""; /* No value key */ 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* XBC parse and tree build */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci unsigned long offset = data - xbc_data; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (WARN_ON(offset >= XBC_DATA_MAX)) 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci node->data = (uint16_t)offset | flag; 41762306a36Sopenharmony_ci node->child = 0; 41862306a36Sopenharmony_ci node->next = 0; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic struct xbc_node * __init xbc_add_node(char *data, uint32_t flag) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct xbc_node *node; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (xbc_node_num == XBC_NODE_MAX) 42862306a36Sopenharmony_ci return NULL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci node = &xbc_nodes[xbc_node_num++]; 43162306a36Sopenharmony_ci if (xbc_init_node(node, data, flag) < 0) 43262306a36Sopenharmony_ci return NULL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return node; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic inline __init struct xbc_node *xbc_last_sibling(struct xbc_node *node) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci while (node->next) 44062306a36Sopenharmony_ci node = xbc_node_get_next(node); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return node; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic inline __init struct xbc_node *xbc_last_child(struct xbc_node *node) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci while (node->child) 44862306a36Sopenharmony_ci node = xbc_node_get_child(node); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return node; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic struct xbc_node * __init __xbc_add_sibling(char *data, uint32_t flag, bool head) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct xbc_node *sib, *node = xbc_add_node(data, flag); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (node) { 45862306a36Sopenharmony_ci if (!last_parent) { 45962306a36Sopenharmony_ci /* Ignore @head in this case */ 46062306a36Sopenharmony_ci node->parent = XBC_NODE_MAX; 46162306a36Sopenharmony_ci sib = xbc_last_sibling(xbc_nodes); 46262306a36Sopenharmony_ci sib->next = xbc_node_index(node); 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci node->parent = xbc_node_index(last_parent); 46562306a36Sopenharmony_ci if (!last_parent->child || head) { 46662306a36Sopenharmony_ci node->next = last_parent->child; 46762306a36Sopenharmony_ci last_parent->child = xbc_node_index(node); 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci sib = xbc_node_get_child(last_parent); 47062306a36Sopenharmony_ci sib = xbc_last_sibling(sib); 47162306a36Sopenharmony_ci sib->next = xbc_node_index(node); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } else 47562306a36Sopenharmony_ci xbc_parse_error("Too many nodes", data); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return node; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic inline struct xbc_node * __init xbc_add_sibling(char *data, uint32_t flag) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return __xbc_add_sibling(data, flag, false); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic inline struct xbc_node * __init xbc_add_head_sibling(char *data, uint32_t flag) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci return __xbc_add_sibling(data, flag, true); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic inline __init struct xbc_node *xbc_add_child(char *data, uint32_t flag) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct xbc_node *node = xbc_add_sibling(data, flag); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (node) 49562306a36Sopenharmony_ci last_parent = node; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return node; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic inline __init bool xbc_valid_keyword(char *key) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci if (key[0] == '\0') 50362306a36Sopenharmony_ci return false; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci while (isalnum(*key) || *key == '-' || *key == '_') 50662306a36Sopenharmony_ci key++; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return *key == '\0'; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic char *skip_comment(char *p) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci char *ret; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ret = strchr(p, '\n'); 51662306a36Sopenharmony_ci if (!ret) 51762306a36Sopenharmony_ci ret = p + strlen(p); 51862306a36Sopenharmony_ci else 51962306a36Sopenharmony_ci ret++; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic char *skip_spaces_until_newline(char *p) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci while (isspace(*p) && *p != '\n') 52762306a36Sopenharmony_ci p++; 52862306a36Sopenharmony_ci return p; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int __init __xbc_open_brace(char *p) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci /* Push the last key as open brace */ 53462306a36Sopenharmony_ci open_brace[brace_index++] = xbc_node_index(last_parent); 53562306a36Sopenharmony_ci if (brace_index >= XBC_DEPTH_MAX) 53662306a36Sopenharmony_ci return xbc_parse_error("Exceed max depth of braces", p); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int __init __xbc_close_brace(char *p) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci brace_index--; 54462306a36Sopenharmony_ci if (!last_parent || brace_index < 0 || 54562306a36Sopenharmony_ci (open_brace[brace_index] != xbc_node_index(last_parent))) 54662306a36Sopenharmony_ci return xbc_parse_error("Unexpected closing brace", p); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (brace_index == 0) 54962306a36Sopenharmony_ci last_parent = NULL; 55062306a36Sopenharmony_ci else 55162306a36Sopenharmony_ci last_parent = &xbc_nodes[open_brace[brace_index - 1]]; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * Return delimiter or error, no node added. As same as lib/cmdline.c, 55862306a36Sopenharmony_ci * you can use " around spaces, but can't escape " for value. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic int __init __xbc_parse_value(char **__v, char **__n) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci char *p, *v = *__v; 56362306a36Sopenharmony_ci int c, quotes = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci v = skip_spaces(v); 56662306a36Sopenharmony_ci while (*v == '#') { 56762306a36Sopenharmony_ci v = skip_comment(v); 56862306a36Sopenharmony_ci v = skip_spaces(v); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci if (*v == '"' || *v == '\'') { 57162306a36Sopenharmony_ci quotes = *v; 57262306a36Sopenharmony_ci v++; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci p = v - 1; 57562306a36Sopenharmony_ci while ((c = *++p)) { 57662306a36Sopenharmony_ci if (!isprint(c) && !isspace(c)) 57762306a36Sopenharmony_ci return xbc_parse_error("Non printable value", p); 57862306a36Sopenharmony_ci if (quotes) { 57962306a36Sopenharmony_ci if (c != quotes) 58062306a36Sopenharmony_ci continue; 58162306a36Sopenharmony_ci quotes = 0; 58262306a36Sopenharmony_ci *p++ = '\0'; 58362306a36Sopenharmony_ci p = skip_spaces_until_newline(p); 58462306a36Sopenharmony_ci c = *p; 58562306a36Sopenharmony_ci if (c && !strchr(",;\n#}", c)) 58662306a36Sopenharmony_ci return xbc_parse_error("No value delimiter", p); 58762306a36Sopenharmony_ci if (*p) 58862306a36Sopenharmony_ci p++; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci if (strchr(",;\n#}", c)) { 59262306a36Sopenharmony_ci *p++ = '\0'; 59362306a36Sopenharmony_ci v = strim(v); 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci if (quotes) 59862306a36Sopenharmony_ci return xbc_parse_error("No closing quotes", p); 59962306a36Sopenharmony_ci if (c == '#') { 60062306a36Sopenharmony_ci p = skip_comment(p); 60162306a36Sopenharmony_ci c = '\n'; /* A comment must be treated as a newline */ 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci *__n = p; 60462306a36Sopenharmony_ci *__v = v; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return c; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int __init xbc_parse_array(char **__v) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct xbc_node *node; 61262306a36Sopenharmony_ci char *next; 61362306a36Sopenharmony_ci int c = 0; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (last_parent->child) 61662306a36Sopenharmony_ci last_parent = xbc_node_get_child(last_parent); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci do { 61962306a36Sopenharmony_ci c = __xbc_parse_value(__v, &next); 62062306a36Sopenharmony_ci if (c < 0) 62162306a36Sopenharmony_ci return c; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci node = xbc_add_child(*__v, XBC_VALUE); 62462306a36Sopenharmony_ci if (!node) 62562306a36Sopenharmony_ci return -ENOMEM; 62662306a36Sopenharmony_ci *__v = next; 62762306a36Sopenharmony_ci } while (c == ','); 62862306a36Sopenharmony_ci node->child = 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return c; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic inline __init 63462306a36Sopenharmony_cistruct xbc_node *find_match_node(struct xbc_node *node, char *k) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci while (node) { 63762306a36Sopenharmony_ci if (!strcmp(xbc_node_get_data(node), k)) 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci node = xbc_node_get_next(node); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci return node; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int __init __xbc_add_key(char *k) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct xbc_node *node, *child; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (!xbc_valid_keyword(k)) 64962306a36Sopenharmony_ci return xbc_parse_error("Invalid keyword", k); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (unlikely(xbc_node_num == 0)) 65262306a36Sopenharmony_ci goto add_node; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!last_parent) /* the first level */ 65562306a36Sopenharmony_ci node = find_match_node(xbc_nodes, k); 65662306a36Sopenharmony_ci else { 65762306a36Sopenharmony_ci child = xbc_node_get_child(last_parent); 65862306a36Sopenharmony_ci /* Since the value node is the first child, skip it. */ 65962306a36Sopenharmony_ci if (child && xbc_node_is_value(child)) 66062306a36Sopenharmony_ci child = xbc_node_get_next(child); 66162306a36Sopenharmony_ci node = find_match_node(child, k); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (node) 66562306a36Sopenharmony_ci last_parent = node; 66662306a36Sopenharmony_ci else { 66762306a36Sopenharmony_ciadd_node: 66862306a36Sopenharmony_ci node = xbc_add_child(k, XBC_KEY); 66962306a36Sopenharmony_ci if (!node) 67062306a36Sopenharmony_ci return -ENOMEM; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int __init __xbc_parse_keys(char *k) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci char *p; 67862306a36Sopenharmony_ci int ret; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci k = strim(k); 68162306a36Sopenharmony_ci while ((p = strchr(k, '.'))) { 68262306a36Sopenharmony_ci *p++ = '\0'; 68362306a36Sopenharmony_ci ret = __xbc_add_key(k); 68462306a36Sopenharmony_ci if (ret) 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci k = p; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return __xbc_add_key(k); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int __init xbc_parse_kv(char **k, char *v, int op) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct xbc_node *prev_parent = last_parent; 69562306a36Sopenharmony_ci struct xbc_node *child; 69662306a36Sopenharmony_ci char *next; 69762306a36Sopenharmony_ci int c, ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ret = __xbc_parse_keys(*k); 70062306a36Sopenharmony_ci if (ret) 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci c = __xbc_parse_value(&v, &next); 70462306a36Sopenharmony_ci if (c < 0) 70562306a36Sopenharmony_ci return c; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci child = xbc_node_get_child(last_parent); 70862306a36Sopenharmony_ci if (child && xbc_node_is_value(child)) { 70962306a36Sopenharmony_ci if (op == '=') 71062306a36Sopenharmony_ci return xbc_parse_error("Value is redefined", v); 71162306a36Sopenharmony_ci if (op == ':') { 71262306a36Sopenharmony_ci unsigned short nidx = child->next; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci xbc_init_node(child, v, XBC_VALUE); 71562306a36Sopenharmony_ci child->next = nidx; /* keep subkeys */ 71662306a36Sopenharmony_ci goto array; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci /* op must be '+' */ 71962306a36Sopenharmony_ci last_parent = xbc_last_child(child); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci /* The value node should always be the first child */ 72262306a36Sopenharmony_ci if (!xbc_add_head_sibling(v, XBC_VALUE)) 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ciarray: 72662306a36Sopenharmony_ci if (c == ',') { /* Array */ 72762306a36Sopenharmony_ci c = xbc_parse_array(&next); 72862306a36Sopenharmony_ci if (c < 0) 72962306a36Sopenharmony_ci return c; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci last_parent = prev_parent; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (c == '}') { 73562306a36Sopenharmony_ci ret = __xbc_close_brace(next - 1); 73662306a36Sopenharmony_ci if (ret < 0) 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci *k = next; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int __init xbc_parse_key(char **k, char *n) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct xbc_node *prev_parent = last_parent; 74862306a36Sopenharmony_ci int ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci *k = strim(*k); 75162306a36Sopenharmony_ci if (**k != '\0') { 75262306a36Sopenharmony_ci ret = __xbc_parse_keys(*k); 75362306a36Sopenharmony_ci if (ret) 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci last_parent = prev_parent; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci *k = n; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int __init xbc_open_brace(char **k, char *n) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci int ret; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = __xbc_parse_keys(*k); 76762306a36Sopenharmony_ci if (ret) 76862306a36Sopenharmony_ci return ret; 76962306a36Sopenharmony_ci *k = n; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return __xbc_open_brace(n - 1); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int __init xbc_close_brace(char **k, char *n) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci ret = xbc_parse_key(k, n); 77962306a36Sopenharmony_ci if (ret) 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci /* k is updated in xbc_parse_key() */ 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return __xbc_close_brace(n - 1); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int __init xbc_verify_tree(void) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci int i, depth, len, wlen; 78962306a36Sopenharmony_ci struct xbc_node *n, *m; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Brace closing */ 79262306a36Sopenharmony_ci if (brace_index) { 79362306a36Sopenharmony_ci n = &xbc_nodes[open_brace[brace_index]]; 79462306a36Sopenharmony_ci return xbc_parse_error("Brace is not closed", 79562306a36Sopenharmony_ci xbc_node_get_data(n)); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* Empty tree */ 79962306a36Sopenharmony_ci if (xbc_node_num == 0) { 80062306a36Sopenharmony_ci xbc_parse_error("Empty config", xbc_data); 80162306a36Sopenharmony_ci return -ENOENT; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci for (i = 0; i < xbc_node_num; i++) { 80562306a36Sopenharmony_ci if (xbc_nodes[i].next > xbc_node_num) { 80662306a36Sopenharmony_ci return xbc_parse_error("No closing brace", 80762306a36Sopenharmony_ci xbc_node_get_data(xbc_nodes + i)); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Key tree limitation check */ 81262306a36Sopenharmony_ci n = &xbc_nodes[0]; 81362306a36Sopenharmony_ci depth = 1; 81462306a36Sopenharmony_ci len = 0; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci while (n) { 81762306a36Sopenharmony_ci wlen = strlen(xbc_node_get_data(n)) + 1; 81862306a36Sopenharmony_ci len += wlen; 81962306a36Sopenharmony_ci if (len > XBC_KEYLEN_MAX) 82062306a36Sopenharmony_ci return xbc_parse_error("Too long key length", 82162306a36Sopenharmony_ci xbc_node_get_data(n)); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci m = xbc_node_get_child(n); 82462306a36Sopenharmony_ci if (m && xbc_node_is_key(m)) { 82562306a36Sopenharmony_ci n = m; 82662306a36Sopenharmony_ci depth++; 82762306a36Sopenharmony_ci if (depth > XBC_DEPTH_MAX) 82862306a36Sopenharmony_ci return xbc_parse_error("Too many key words", 82962306a36Sopenharmony_ci xbc_node_get_data(n)); 83062306a36Sopenharmony_ci continue; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci len -= wlen; 83362306a36Sopenharmony_ci m = xbc_node_get_next(n); 83462306a36Sopenharmony_ci while (!m) { 83562306a36Sopenharmony_ci n = xbc_node_get_parent(n); 83662306a36Sopenharmony_ci if (!n) 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci len -= strlen(xbc_node_get_data(n)) + 1; 83962306a36Sopenharmony_ci depth--; 84062306a36Sopenharmony_ci m = xbc_node_get_next(n); 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci n = m; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* Need to setup xbc_data and xbc_nodes before call this. */ 84962306a36Sopenharmony_cistatic int __init xbc_parse_tree(void) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci char *p, *q; 85262306a36Sopenharmony_ci int ret = 0, c; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci last_parent = NULL; 85562306a36Sopenharmony_ci p = xbc_data; 85662306a36Sopenharmony_ci do { 85762306a36Sopenharmony_ci q = strpbrk(p, "{}=+;:\n#"); 85862306a36Sopenharmony_ci if (!q) { 85962306a36Sopenharmony_ci p = skip_spaces(p); 86062306a36Sopenharmony_ci if (*p != '\0') 86162306a36Sopenharmony_ci ret = xbc_parse_error("No delimiter", p); 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci c = *q; 86662306a36Sopenharmony_ci *q++ = '\0'; 86762306a36Sopenharmony_ci switch (c) { 86862306a36Sopenharmony_ci case ':': 86962306a36Sopenharmony_ci case '+': 87062306a36Sopenharmony_ci if (*q++ != '=') { 87162306a36Sopenharmony_ci ret = xbc_parse_error(c == '+' ? 87262306a36Sopenharmony_ci "Wrong '+' operator" : 87362306a36Sopenharmony_ci "Wrong ':' operator", 87462306a36Sopenharmony_ci q - 2); 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci fallthrough; 87862306a36Sopenharmony_ci case '=': 87962306a36Sopenharmony_ci ret = xbc_parse_kv(&p, q, c); 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci case '{': 88262306a36Sopenharmony_ci ret = xbc_open_brace(&p, q); 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci case '#': 88562306a36Sopenharmony_ci q = skip_comment(q); 88662306a36Sopenharmony_ci fallthrough; 88762306a36Sopenharmony_ci case ';': 88862306a36Sopenharmony_ci case '\n': 88962306a36Sopenharmony_ci ret = xbc_parse_key(&p, q); 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci case '}': 89262306a36Sopenharmony_ci ret = xbc_close_brace(&p, q); 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci } while (!ret); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/** 90162306a36Sopenharmony_ci * xbc_exit() - Clean up all parsed bootconfig 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * This clears all data structures of parsed bootconfig on memory. 90462306a36Sopenharmony_ci * If you need to reuse xbc_init() with new boot config, you can 90562306a36Sopenharmony_ci * use this. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_civoid __init xbc_exit(void) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci xbc_free_mem(xbc_data, xbc_data_size); 91062306a36Sopenharmony_ci xbc_data = NULL; 91162306a36Sopenharmony_ci xbc_data_size = 0; 91262306a36Sopenharmony_ci xbc_node_num = 0; 91362306a36Sopenharmony_ci xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX); 91462306a36Sopenharmony_ci xbc_nodes = NULL; 91562306a36Sopenharmony_ci brace_index = 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci/** 91962306a36Sopenharmony_ci * xbc_init() - Parse given XBC file and build XBC internal tree 92062306a36Sopenharmony_ci * @data: The boot config text original data 92162306a36Sopenharmony_ci * @size: The size of @data 92262306a36Sopenharmony_ci * @emsg: A pointer of const char * to store the error message 92362306a36Sopenharmony_ci * @epos: A pointer of int to store the error position 92462306a36Sopenharmony_ci * 92562306a36Sopenharmony_ci * This parses the boot config text in @data. @size must be smaller 92662306a36Sopenharmony_ci * than XBC_DATA_MAX. 92762306a36Sopenharmony_ci * Return the number of stored nodes (>0) if succeeded, or -errno 92862306a36Sopenharmony_ci * if there is any error. 92962306a36Sopenharmony_ci * In error cases, @emsg will be updated with an error message and 93062306a36Sopenharmony_ci * @epos will be updated with the error position which is the byte offset 93162306a36Sopenharmony_ci * of @buf. If the error is not a parser error, @epos will be -1. 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_ciint __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci int ret; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (epos) 93862306a36Sopenharmony_ci *epos = -1; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (xbc_data) { 94162306a36Sopenharmony_ci if (emsg) 94262306a36Sopenharmony_ci *emsg = "Bootconfig is already initialized"; 94362306a36Sopenharmony_ci return -EBUSY; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci if (size > XBC_DATA_MAX || size == 0) { 94662306a36Sopenharmony_ci if (emsg) 94762306a36Sopenharmony_ci *emsg = size ? "Config data is too big" : 94862306a36Sopenharmony_ci "Config data is empty"; 94962306a36Sopenharmony_ci return -ERANGE; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci xbc_data = xbc_alloc_mem(size + 1); 95362306a36Sopenharmony_ci if (!xbc_data) { 95462306a36Sopenharmony_ci if (emsg) 95562306a36Sopenharmony_ci *emsg = "Failed to allocate bootconfig data"; 95662306a36Sopenharmony_ci return -ENOMEM; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci memcpy(xbc_data, data, size); 95962306a36Sopenharmony_ci xbc_data[size] = '\0'; 96062306a36Sopenharmony_ci xbc_data_size = size + 1; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci xbc_nodes = xbc_alloc_mem(sizeof(struct xbc_node) * XBC_NODE_MAX); 96362306a36Sopenharmony_ci if (!xbc_nodes) { 96462306a36Sopenharmony_ci if (emsg) 96562306a36Sopenharmony_ci *emsg = "Failed to allocate bootconfig nodes"; 96662306a36Sopenharmony_ci xbc_exit(); 96762306a36Sopenharmony_ci return -ENOMEM; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci ret = xbc_parse_tree(); 97262306a36Sopenharmony_ci if (!ret) 97362306a36Sopenharmony_ci ret = xbc_verify_tree(); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (ret < 0) { 97662306a36Sopenharmony_ci if (epos) 97762306a36Sopenharmony_ci *epos = xbc_err_pos; 97862306a36Sopenharmony_ci if (emsg) 97962306a36Sopenharmony_ci *emsg = xbc_err_msg; 98062306a36Sopenharmony_ci xbc_exit(); 98162306a36Sopenharmony_ci } else 98262306a36Sopenharmony_ci ret = xbc_node_num; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return ret; 98562306a36Sopenharmony_ci} 986