993 lines · 23.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Helpers for initial module or kernel cmdline parsing * Copyright (C) 2001 Rusty Russell. */#include <linux/ctype.h>#include <linux/device.h>#include <linux/err.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/kstrtox.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/overflow.h>#include <linux/security.h>#include <linux/slab.h>#include <linux/string.h> #ifdef CONFIG_SYSFS/* Protects all built-in parameters, modules use their own param_lock */static DEFINE_MUTEX(param_lock); /* Use the module's mutex, or if built-in use the built-in mutex */#ifdef CONFIG_MODULES#define KPARAM_MUTEX(mod)	((mod) ? &(mod)->param_lock : &param_lock)#else#define KPARAM_MUTEX(mod)	(&param_lock)#endif static inline void check_kparam_locked(struct module *mod){	BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod)));}#elsestatic inline void check_kparam_locked(struct module *mod){}#endif /* !CONFIG_SYSFS */ /* This just allows us to keep track of which parameters are kmalloced. */struct kmalloced_param {	struct list_head list;	char val[];};static LIST_HEAD(kmalloced_params);static DEFINE_SPINLOCK(kmalloced_params_lock); static void *kmalloc_parameter(unsigned int size){	struct kmalloced_param *p; 	p = kmalloc(size_add(sizeof(*p), size), GFP_KERNEL);	if (!p)		return NULL; 	spin_lock(&kmalloced_params_lock);	list_add(&p->list, &kmalloced_params);	spin_unlock(&kmalloced_params_lock); 	return p->val;} /* Does nothing if parameter wasn't kmalloced above. */static void maybe_kfree_parameter(void *param){	struct kmalloced_param *p; 	spin_lock(&kmalloced_params_lock);	list_for_each_entry(p, &kmalloced_params, list) {		if (p->val == param) {			list_del(&p->list);			kfree(p);			break;		}	}	spin_unlock(&kmalloced_params_lock);} static char dash2underscore(char c){	if (c == '-')		return '_';	return c;} bool parameqn(const char *a, const char *b, size_t n){	size_t i; 	for (i = 0; i < n; i++) {		if (dash2underscore(a[i]) != dash2underscore(b[i]))			return false;	}	return true;} bool parameq(const char *a, const char *b){	return parameqn(a, b, strlen(a)+1);} static bool param_check_unsafe(const struct kernel_param *kp){	if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&	    security_locked_down(LOCKDOWN_MODULE_PARAMETERS))		return false; 	if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {		pr_notice("Setting dangerous option %s - tainting kernel\n",			  kp->name);		add_taint(TAINT_USER, LOCKDEP_STILL_OK);	} 	return true;} static int parse_one(char *param,		     char *val,		     const char *doing,		     const struct kernel_param *params,		     unsigned num_params,		     s16 min_level,		     s16 max_level,		     void *arg, parse_unknown_fn handle_unknown){	unsigned int i;	int err; 	/* Find parameter */	for (i = 0; i < num_params; i++) {		if (parameq(param, params[i].name)) {			if (params[i].level < min_level			    || params[i].level > max_level)				return 0;			/* No one handled NULL, so do it here. */			if (!val &&			    !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))				return -EINVAL;			pr_debug("handling %s with %p\n", param,				params[i].ops->set);			kernel_param_lock(params[i].mod);			if (param_check_unsafe(&params[i]))				err = params[i].ops->set(val, &params[i]);			else				err = -EPERM;			kernel_param_unlock(params[i].mod);			return err;		}	} 	if (handle_unknown) {		pr_debug("doing %s: %s='%s'\n", doing, param, val);		return handle_unknown(param, val, doing, arg);	} 	pr_debug("Unknown argument '%s'\n", param);	return -ENOENT;} /* Args looks like "foo=bar,bar2 baz=fuz wiz". */char *parse_args(const char *doing,		 char *args,		 const struct kernel_param *params,		 unsigned num,		 s16 min_level,		 s16 max_level,		 void *arg, parse_unknown_fn unknown){	char *param, *val, *err = NULL; 	/* Chew leading spaces */	args = skip_spaces(args); 	if (*args)		pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args); 	while (*args) {		int ret;		int irq_was_disabled; 		args = next_arg(args, &param, &val);		/* Stop at -- */		if (!val && strcmp(param, "--") == 0)			return err ?: args;		irq_was_disabled = irqs_disabled();		ret = parse_one(param, val, doing, params, num,				min_level, max_level, arg, unknown);		if (irq_was_disabled && !irqs_disabled())			pr_warn("%s: option '%s' enabled irq's!\n",				doing, param); 		switch (ret) {		case 0:			continue;		case -ENOENT:			pr_err("%s: Unknown parameter `%s'\n", doing, param);			break;		case -ENOSPC:			pr_err("%s: `%s' too large for parameter `%s'\n",			       doing, val ?: "", param);			break;		default:			pr_err("%s: `%s' invalid for parameter `%s'\n",			       doing, val ?: "", param);			break;		} 		err = ERR_PTR(ret);	} 	return err;} /* Lazy bastard, eh? */#define STANDARD_PARAM_DEF(name, type, format, strtolfn)      		\	int param_set_##name(const char *val, const struct kernel_param *kp) \	{								\		return strtolfn(val, 0, (type *)kp->arg);		\	}								\	int param_get_##name(char *buffer, const struct kernel_param *kp) \	{								\		return scnprintf(buffer, PAGE_SIZE, format "\n",	\				*((type *)kp->arg));			\	}								\	const struct kernel_param_ops param_ops_##name = {			\		.set = param_set_##name,				\		.get = param_get_##name,				\	};								\	EXPORT_SYMBOL(param_set_##name);				\	EXPORT_SYMBOL(param_get_##name);				\	EXPORT_SYMBOL(param_ops_##name)  STANDARD_PARAM_DEF(byte,	unsigned char,		"%hhu",		kstrtou8);STANDARD_PARAM_DEF(short,	short,			"%hi",		kstrtos16);STANDARD_PARAM_DEF(ushort,	unsigned short,		"%hu",		kstrtou16);STANDARD_PARAM_DEF(int,		int,			"%i",		kstrtoint);STANDARD_PARAM_DEF(uint,	unsigned int,		"%u",		kstrtouint);STANDARD_PARAM_DEF(long,	long,			"%li",		kstrtol);STANDARD_PARAM_DEF(ulong,	unsigned long,		"%lu",		kstrtoul);STANDARD_PARAM_DEF(ullong,	unsigned long long,	"%llu",		kstrtoull);STANDARD_PARAM_DEF(hexint,	unsigned int,		"%#08x", 	kstrtouint); int param_set_uint_minmax(const char *val, const struct kernel_param *kp,		unsigned int min, unsigned int max){	unsigned int num;	int ret; 	if (!val)		return -EINVAL;	ret = kstrtouint(val, 0, &num);	if (ret)		return ret;	if (num < min || num > max)		return -EINVAL;	*((unsigned int *)kp->arg) = num;	return 0;}EXPORT_SYMBOL_GPL(param_set_uint_minmax); int param_set_charp(const char *val, const struct kernel_param *kp){	size_t len, maxlen = 1024; 	len = strnlen(val, maxlen + 1);	if (len == maxlen + 1) {		pr_err("%s: string parameter too long\n", kp->name);		return -ENOSPC;	} 	maybe_kfree_parameter(*(char **)kp->arg); 	/*	 * This is a hack. We can't kmalloc() in early boot, and we	 * don't need to; this mangled commandline is preserved.	 */	if (slab_is_available()) {		*(char **)kp->arg = kmalloc_parameter(len + 1);		if (!*(char **)kp->arg)			return -ENOMEM;		strcpy(*(char **)kp->arg, val);	} else		*(const char **)kp->arg = val; 	return 0;}EXPORT_SYMBOL(param_set_charp); int param_get_charp(char *buffer, const struct kernel_param *kp){	return scnprintf(buffer, PAGE_SIZE, "%s\n", *((char **)kp->arg));}EXPORT_SYMBOL(param_get_charp); void param_free_charp(void *arg){	maybe_kfree_parameter(*((char **)arg));}EXPORT_SYMBOL(param_free_charp); const struct kernel_param_ops param_ops_charp = {	.set = param_set_charp,	.get = param_get_charp,	.free = param_free_charp,};EXPORT_SYMBOL(param_ops_charp); /* Actually could be a bool or an int, for historical reasons. */int param_set_bool(const char *val, const struct kernel_param *kp){	/* No equals means "set"... */	if (!val) val = "1"; 	/* One of =[yYnN01] */	return kstrtobool(val, kp->arg);}EXPORT_SYMBOL(param_set_bool); int param_get_bool(char *buffer, const struct kernel_param *kp){	/* Y and N chosen as being relatively non-coder friendly */	return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N');}EXPORT_SYMBOL(param_get_bool); const struct kernel_param_ops param_ops_bool = {	.flags = KERNEL_PARAM_OPS_FL_NOARG,	.set = param_set_bool,	.get = param_get_bool,};EXPORT_SYMBOL(param_ops_bool); int param_set_bool_enable_only(const char *val, const struct kernel_param *kp){	int err;	bool new_value;	bool orig_value = *(bool *)kp->arg;	struct kernel_param dummy_kp = *kp; 	dummy_kp.arg = &new_value; 	err = param_set_bool(val, &dummy_kp);	if (err)		return err; 	/* Don't let them unset it once it's set! */	if (!new_value && orig_value)		return -EROFS; 	if (new_value)		err = param_set_bool(val, kp); 	return err;}EXPORT_SYMBOL_GPL(param_set_bool_enable_only); const struct kernel_param_ops param_ops_bool_enable_only = {	.flags = KERNEL_PARAM_OPS_FL_NOARG,	.set = param_set_bool_enable_only,	.get = param_get_bool,};EXPORT_SYMBOL_GPL(param_ops_bool_enable_only); /* This one must be bool. */int param_set_invbool(const char *val, const struct kernel_param *kp){	int ret;	bool boolval;	struct kernel_param dummy; 	dummy.arg = &boolval;	ret = param_set_bool(val, &dummy);	if (ret == 0)		*(bool *)kp->arg = !boolval;	return ret;}EXPORT_SYMBOL(param_set_invbool); int param_get_invbool(char *buffer, const struct kernel_param *kp){	return sprintf(buffer, "%c\n", (*(bool *)kp->arg) ? 'N' : 'Y');}EXPORT_SYMBOL(param_get_invbool); const struct kernel_param_ops param_ops_invbool = {	.set = param_set_invbool,	.get = param_get_invbool,};EXPORT_SYMBOL(param_ops_invbool); int param_set_bint(const char *val, const struct kernel_param *kp){	/* Match bool exactly, by re-using it. */	struct kernel_param boolkp = *kp;	bool v;	int ret; 	boolkp.arg = &v; 	ret = param_set_bool(val, &boolkp);	if (ret == 0)		*(int *)kp->arg = v;	return ret;}EXPORT_SYMBOL(param_set_bint); const struct kernel_param_ops param_ops_bint = {	.flags = KERNEL_PARAM_OPS_FL_NOARG,	.set = param_set_bint,	.get = param_get_int,};EXPORT_SYMBOL(param_ops_bint); /* We break the rule and mangle the string. */static int param_array(struct module *mod,		       const char *name,		       const char *val,		       unsigned int min, unsigned int max,		       void *elem, int elemsize,		       int (*set)(const char *, const struct kernel_param *kp),		       s16 level,		       unsigned int *num){	int ret;	struct kernel_param kp;	char save; 	/* Get the name right for errors. */	kp.name = name;	kp.arg = elem;	kp.level = level; 	*num = 0;	/* We expect a comma-separated list of values. */	do {		int len; 		if (*num == max) {			pr_err("%s: can only take %i arguments\n", name, max);			return -EINVAL;		}		len = strcspn(val, ","); 		/* nul-terminate and parse */		save = val[len];		((char *)val)[len] = '\0';		check_kparam_locked(mod);		ret = set(val, &kp); 		if (ret != 0)			return ret;		kp.arg += elemsize;		val += len+1;		(*num)++;	} while (save == ','); 	if (*num < min) {		pr_err("%s: needs at least %i arguments\n", name, min);		return -EINVAL;	}	return 0;} static int param_array_set(const char *val, const struct kernel_param *kp){	const struct kparam_array *arr = kp->arr;	unsigned int temp_num; 	return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,			   arr->elemsize, arr->ops->set, kp->level,			   arr->num ?: &temp_num);} static int param_array_get(char *buffer, const struct kernel_param *kp){	int i, off, ret;	const struct kparam_array *arr = kp->arr;	struct kernel_param p = *kp; 	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {		/* Replace \n with comma */		if (i)			buffer[off - 1] = ',';		p.arg = arr->elem + arr->elemsize * i;		check_kparam_locked(p.mod);		ret = arr->ops->get(buffer + off, &p);		if (ret < 0)			return ret;		off += ret;	}	buffer[off] = '\0';	return off;} static void param_array_free(void *arg){	unsigned int i;	const struct kparam_array *arr = arg; 

Lines 1–500 of 993.