// SPDX-License-Identifier: GPL-2.0
/*
* Extra Boot Config
* Masami Hiramatsu <mhiramat@kernel.org>
*/
/*
* NOTE: This is only for tools/bootconfig, because tools/bootconfig will
* run the parser sanity test.
* This does NOT mean lib/bootconfig.c is available in the user space.
* However, if you change this file, please make sure the tools/bootconfig
* has no issue on building and running.
*/
#include <linux/bootconfig.h>
#ifdef __KERNEL__
#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/cache.h>
#include <linux/compiler.h>
#include <linux/sprintf.h>
#include <linux/memblock.h>
#include <linux/string.h>
#ifdef CONFIG_BOOT_CONFIG_EMBED
/* embedded_bootconfig_data is defined in bootconfig-data.S */
extern __visible const char embedded_bootconfig_data[];
extern __visible const char embedded_bootconfig_data_end[];
const char * __init xbc_get_embedded_bootconfig(size_t *size)
{
*size = embedded_bootconfig_data_end - embedded_bootconfig_data;
return (*size) ? embedded_bootconfig_data : NULL;
}
#endif
#endif
/*
* Extra Boot Config (XBC) is given as tree-structured ascii text of
* key-value pairs on memory.
* xbc_parse() parses the text to build a simple tree. Each tree node is
* simply a key word or a value. A key node may have a next key node or/and
* a child node (both key and value). A value node may have a next value
* node (for array).
*/
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 open_brace[XBC_DEPTH_MAX] __initdata;
static int brace_index __initdata;
#ifdef __KERNEL__
static inline void * __init xbc_alloc_mem(size_t size)
{
return memblock_alloc(size, SMP_CACHE_BYTES);
}
static inline void __init xbc_free_mem(void *addr, size_t size, bool early)
{
if (early)
memblock_free(addr, size);
else if (addr)
memblock_free(addr, size);
}
#else /* !__KERNEL__ */
static inline void *xbc_alloc_mem(size_t size)
{
return calloc(1, size);
}
static inline void xbc_free_mem(void *addr, size_t size, bool early)
{
free(addr);
}
#endif
/**
* xbc_get_info() - Get the information of loaded boot config
* @node_size: A pointer to store the number of nodes.
* @data_size: A pointer to store the size of bootconfig data.
*
* Get the number of used nodes in @node_size if it is not NULL,
* and the size of bootconfig data in @data_size if it is not NULL.
* Return 0 if the boot config is initialized, or return -ENODEV.
*/
int __init xbc_get_info(int *node_size, size_t *data_size)
{
if (!xbc_data)
return -ENODEV;
if (node_size)
*node_size = xbc_node_num;
if (data_size)
*data_size = xbc_data_size;
return 0;
}
static int __init xbc_parse_error(const char *msg, const char *p)
{
xbc_err_msg = msg;
xbc_err_pos = (int)(p - xbc_data);
return -EINVAL;
}
/**
* xbc_root_node() - Get the root node of extended boot config
*
* Return the address of root node of extended boot config. If the
* extended boot config is not initialized, return NULL.
*/
struct xbc_node * __init xbc_root_node(void)
{
if (unlikely(!xbc_data))
return NULL;
return xbc_nodes;
}
/**
* xbc_node_index() - Get the index of XBC node
* @node: A target node of getting index.
*
* Return the index number of @node in XBC node list.
*/
uint16_t __init xbc_node_index(struct xbc_node *node)
{
return (uint16_t)(node - &xbc_nodes[0]);
}
/**
* xbc_node_get_parent() - Get the parent XBC node
* @node: An XBC node.
*
* Return the parent node of @node. If the node is top node of the tree,
* return NULL.
*/
struct xbc_node * __init xbc_node_get_parent(struct xbc_node *node)
{
return node->parent == XBC_NODE_MAX ? NULL : &xbc_nodes[node->parent];
}
/**
* xbc_node_get_child() - Get the child XBC node
* @node: An XBC node.
*
* Return the first child node of @node. If the node has no child, return
* NULL.
*/
struct xbc_node * __init xbc_node_get_child(struct xbc_node *node)
{
return node->child ? &xbc_nodes[node->child] : NULL;
}
/**
* xbc_node_get_next() - Get the next sibling XBC node
* @node: An XBC node.
*
* Return the NEXT sibling node of @node. If the node has no next sibling,
* return NULL. Note that even if this returns NULL, it doesn't mean @node
* has no siblings. (You also has to check whether the parent's child node
* is @node or not.)
*/
struct xbc_node * __init xbc_node_get_next(struct xbc_node *node)
{
return node->next ? &xbc_nodes[node->next] : NULL;
}
/**
* xbc_node_get_data() - Get the data of XBC node
* @node: An XBC node.
*
* Return the data (which is always a null terminated string) of @node.
* If the node has invalid data, warn and return NULL.
*/
const char * __init xbc_node_get_data(struct xbc_node *node)
{
size_t offset