diff options
Diffstat (limited to 'fs/proc/bootconfig.c')
| -rw-r--r-- | fs/proc/bootconfig.c | 89 | 
1 files changed, 89 insertions, 0 deletions
diff --git a/fs/proc/bootconfig.c b/fs/proc/bootconfig.c new file mode 100644 index 000000000000..9955d75c0585 --- /dev/null +++ b/fs/proc/bootconfig.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * /proc/bootconfig - Extra boot configuration + */ +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/printk.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/bootconfig.h> +#include <linux/slab.h> + +static char *saved_boot_config; + +static int boot_config_proc_show(struct seq_file *m, void *v) +{ +	if (saved_boot_config) +		seq_puts(m, saved_boot_config); +	return 0; +} + +/* Rest size of buffer */ +#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0) + +/* Return the needed total length if @size is 0 */ +static int __init copy_xbc_key_value_list(char *dst, size_t size) +{ +	struct xbc_node *leaf, *vnode; +	const char *val; +	char *key, *end = dst + size; +	int ret = 0; + +	key = kzalloc(XBC_KEYLEN_MAX, GFP_KERNEL); + +	xbc_for_each_key_value(leaf, val) { +		ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX); +		if (ret < 0) +			break; +		ret = snprintf(dst, rest(dst, end), "%s = ", key); +		if (ret < 0) +			break; +		dst += ret; +		vnode = xbc_node_get_child(leaf); +		if (vnode && xbc_node_is_array(vnode)) { +			xbc_array_for_each_value(vnode, val) { +				ret = snprintf(dst, rest(dst, end), "\"%s\"%s", +					val, vnode->next ? ", " : "\n"); +				if (ret < 0) +					goto out; +				dst += ret; +			} +		} else { +			ret = snprintf(dst, rest(dst, end), "\"%s\"\n", val); +			if (ret < 0) +				break; +			dst += ret; +		} +	} +out: +	kfree(key); + +	return ret < 0 ? ret : dst - (end - size); +} + +static int __init proc_boot_config_init(void) +{ +	int len; + +	len = copy_xbc_key_value_list(NULL, 0); +	if (len < 0) +		return len; + +	if (len > 0) { +		saved_boot_config = kzalloc(len + 1, GFP_KERNEL); +		if (!saved_boot_config) +			return -ENOMEM; + +		len = copy_xbc_key_value_list(saved_boot_config, len + 1); +		if (len < 0) { +			kfree(saved_boot_config); +			return len; +		} +	} + +	proc_create_single("bootconfig", 0, NULL, boot_config_proc_show); + +	return 0; +} +fs_initcall(proc_boot_config_init);  |