xref: /openbmc/linux/kernel/params.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /* Helpers for initial module or kernel cmdline parsing
31da177e4SLinus Torvalds    Copyright (C) 2001 Rusty Russell.
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds #include <linux/kernel.h>
7def7b92eSChristophe JAILLET #include <linux/kstrtox.h>
81da177e4SLinus Torvalds #include <linux/string.h>
91da177e4SLinus Torvalds #include <linux/errno.h>
101da177e4SLinus Torvalds #include <linux/module.h>
1163a12d9dSGeert Uytterhoeven #include <linux/moduleparam.h>
121da177e4SLinus Torvalds #include <linux/device.h>
131da177e4SLinus Torvalds #include <linux/err.h>
144e57b681STim Schmielau #include <linux/slab.h>
1526d052bfSPeter Oberparleiter #include <linux/ctype.h>
1620657f66SDavid Howells #include <linux/security.h>
171da177e4SLinus Torvalds 
18cf2fde7bSRusty Russell #ifdef CONFIG_SYSFS
19b51d23e4SDan Streetman /* Protects all built-in parameters, modules use their own param_lock */
20907b29ebSRusty Russell static DEFINE_MUTEX(param_lock);
21907b29ebSRusty Russell 
22b51d23e4SDan Streetman /* Use the module's mutex, or if built-in use the built-in mutex */
2320bdc2cfSStephen Rothwell #ifdef CONFIG_MODULES
24b51d23e4SDan Streetman #define KPARAM_MUTEX(mod)	((mod) ? &(mod)->param_lock : &param_lock)
2520bdc2cfSStephen Rothwell #else
2620bdc2cfSStephen Rothwell #define KPARAM_MUTEX(mod)	(&param_lock)
2720bdc2cfSStephen Rothwell #endif
28cf2fde7bSRusty Russell 
check_kparam_locked(struct module * mod)29cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
30cf2fde7bSRusty Russell {
31cf2fde7bSRusty Russell 	BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod)));
32cf2fde7bSRusty Russell }
33cf2fde7bSRusty Russell #else
check_kparam_locked(struct module * mod)34cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
35cf2fde7bSRusty Russell {
36cf2fde7bSRusty Russell }
37cf2fde7bSRusty Russell #endif /* !CONFIG_SYSFS */
38b51d23e4SDan Streetman 
39a1054322SRusty Russell /* This just allows us to keep track of which parameters are kmalloced. */
40a1054322SRusty Russell struct kmalloced_param {
41a1054322SRusty Russell 	struct list_head list;
42a1054322SRusty Russell 	char val[];
43a1054322SRusty Russell };
44a1054322SRusty Russell static LIST_HEAD(kmalloced_params);
45b51d23e4SDan Streetman static DEFINE_SPINLOCK(kmalloced_params_lock);
46a1054322SRusty Russell 
kmalloc_parameter(unsigned int size)47a1054322SRusty Russell static void *kmalloc_parameter(unsigned int size)
48a1054322SRusty Russell {
49a1054322SRusty Russell 	struct kmalloced_param *p;
50a1054322SRusty Russell 
51a1054322SRusty Russell 	p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
52a1054322SRusty Russell 	if (!p)
53a1054322SRusty Russell 		return NULL;
54a1054322SRusty Russell 
55b51d23e4SDan Streetman 	spin_lock(&kmalloced_params_lock);
56a1054322SRusty Russell 	list_add(&p->list, &kmalloced_params);
57b51d23e4SDan Streetman 	spin_unlock(&kmalloced_params_lock);
58b51d23e4SDan Streetman 
59a1054322SRusty Russell 	return p->val;
60a1054322SRusty Russell }
61a1054322SRusty Russell 
62a1054322SRusty Russell /* Does nothing if parameter wasn't kmalloced above. */
maybe_kfree_parameter(void * param)63a1054322SRusty Russell static void maybe_kfree_parameter(void *param)
64a1054322SRusty Russell {
65a1054322SRusty Russell 	struct kmalloced_param *p;
66a1054322SRusty Russell 
67b51d23e4SDan Streetman 	spin_lock(&kmalloced_params_lock);
68a1054322SRusty Russell 	list_for_each_entry(p, &kmalloced_params, list) {
69a1054322SRusty Russell 		if (p->val == param) {
70a1054322SRusty Russell 			list_del(&p->list);
71a1054322SRusty Russell 			kfree(p);
72a1054322SRusty Russell 			break;
73a1054322SRusty Russell 		}
74a1054322SRusty Russell 	}
75b51d23e4SDan Streetman 	spin_unlock(&kmalloced_params_lock);
76a1054322SRusty Russell }
77a1054322SRusty Russell 
dash2underscore(char c)78b1e4d20cSMichal Schmidt static char dash2underscore(char c)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	if (c == '-')
811da177e4SLinus Torvalds 		return '_';
821da177e4SLinus Torvalds 	return c;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds 
parameqn(const char * a,const char * b,size_t n)85b1e4d20cSMichal Schmidt bool parameqn(const char *a, const char *b, size_t n)
861da177e4SLinus Torvalds {
87b1e4d20cSMichal Schmidt 	size_t i;
88b1e4d20cSMichal Schmidt 
89b1e4d20cSMichal Schmidt 	for (i = 0; i < n; i++) {
90b1e4d20cSMichal Schmidt 		if (dash2underscore(a[i]) != dash2underscore(b[i]))
91b1e4d20cSMichal Schmidt 			return false;
92b1e4d20cSMichal Schmidt 	}
93b1e4d20cSMichal Schmidt 	return true;
94b1e4d20cSMichal Schmidt }
95b1e4d20cSMichal Schmidt 
parameq(const char * a,const char * b)96b1e4d20cSMichal Schmidt bool parameq(const char *a, const char *b)
97b1e4d20cSMichal Schmidt {
98b1e4d20cSMichal Schmidt 	return parameqn(a, b, strlen(a)+1);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
param_check_unsafe(const struct kernel_param * kp)10120657f66SDavid Howells static bool param_check_unsafe(const struct kernel_param *kp)
1027a486d37SRusty Russell {
10320657f66SDavid Howells 	if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&
10420657f66SDavid Howells 	    security_locked_down(LOCKDOWN_MODULE_PARAMETERS))
10520657f66SDavid Howells 		return false;
10620657f66SDavid Howells 
1077a486d37SRusty Russell 	if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
108edc41b3cSChris Wilson 		pr_notice("Setting dangerous option %s - tainting kernel\n",
1097a486d37SRusty Russell 			  kp->name);
1107a486d37SRusty Russell 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
1117a486d37SRusty Russell 	}
11220657f66SDavid Howells 
11320657f66SDavid Howells 	return true;
1147a486d37SRusty Russell }
1157a486d37SRusty Russell 
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,int (* handle_unknown)(char * param,char * val,const char * doing,void * arg))1161da177e4SLinus Torvalds static int parse_one(char *param,
1171da177e4SLinus Torvalds 		     char *val,
1189fb48c74SJim Cromie 		     const char *doing,
119914dcaa8SRusty Russell 		     const struct kernel_param *params,
1201da177e4SLinus Torvalds 		     unsigned num_params,
121026cee00SPawel Moll 		     s16 min_level,
122026cee00SPawel Moll 		     s16 max_level,
123ecc86170SLuis R. Rodriguez 		     void *arg,
1249fb48c74SJim Cromie 		     int (*handle_unknown)(char *param, char *val,
125ecc86170SLuis R. Rodriguez 				     const char *doing, void *arg))
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	unsigned int i;
128907b29ebSRusty Russell 	int err;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	/* Find parameter */
1311da177e4SLinus Torvalds 	for (i = 0; i < num_params; i++) {
1321da177e4SLinus Torvalds 		if (parameq(param, params[i].name)) {
133026cee00SPawel Moll 			if (params[i].level < min_level
134026cee00SPawel Moll 			    || params[i].level > max_level)
135026cee00SPawel Moll 				return 0;
1362e9fb995SRusty Russell 			/* No one handled NULL, so do it here. */
137ab013c5fSSteven Rostedt 			if (!val &&
1386a4c2643SJani Nikula 			    !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
1392e9fb995SRusty Russell 				return -EINVAL;
1409fb48c74SJim Cromie 			pr_debug("handling %s with %p\n", param,
1419bbb9e5aSRusty Russell 				params[i].ops->set);
142b51d23e4SDan Streetman 			kernel_param_lock(params[i].mod);
14320657f66SDavid Howells 			if (param_check_unsafe(&params[i]))
144907b29ebSRusty Russell 				err = params[i].ops->set(val, &params[i]);
14520657f66SDavid Howells 			else
14620657f66SDavid Howells 				err = -EPERM;
147b51d23e4SDan Streetman 			kernel_param_unlock(params[i].mod);
148907b29ebSRusty Russell 			return err;
1491da177e4SLinus Torvalds 		}
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	if (handle_unknown) {
1539fb48c74SJim Cromie 		pr_debug("doing %s: %s='%s'\n", doing, param, val);
154ecc86170SLuis R. Rodriguez 		return handle_unknown(param, val, doing, arg);
1551da177e4SLinus Torvalds 	}
1561da177e4SLinus Torvalds 
1579fb48c74SJim Cromie 	pr_debug("Unknown argument '%s'\n", param);
1581da177e4SLinus Torvalds 	return -ENOENT;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
parse_args(const char * doing,char * args,const struct kernel_param * params,unsigned num,s16 min_level,s16 max_level,void * arg,int (* unknown)(char * param,char * val,const char * doing,void * arg))16251e158c1SRusty Russell char *parse_args(const char *doing,
1631da177e4SLinus Torvalds 		 char *args,
164914dcaa8SRusty Russell 		 const struct kernel_param *params,
1651da177e4SLinus Torvalds 		 unsigned num,
166026cee00SPawel Moll 		 s16 min_level,
167026cee00SPawel Moll 		 s16 max_level,
168ecc86170SLuis R. Rodriguez 		 void *arg,
169ecc86170SLuis R. Rodriguez 		 int (*unknown)(char *param, char *val,
170ecc86170SLuis R. Rodriguez 				const char *doing, void *arg))
1711da177e4SLinus Torvalds {
17274b22c46SOleg Nesterov 	char *param, *val, *err = NULL;
1731da177e4SLinus Torvalds 
174f36462f0SRusty Russell 	/* Chew leading spaces */
175e7d2860bSAndré Goddard Rosa 	args = skip_spaces(args);
176f36462f0SRusty Russell 
1771ef9eaf2SJim Cromie 	if (*args)
1789fb48c74SJim Cromie 		pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
1799fb48c74SJim Cromie 
1801da177e4SLinus Torvalds 	while (*args) {
1811da177e4SLinus Torvalds 		int ret;
182a416aba6SArd van Breemen 		int irq_was_disabled;
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 		args = next_arg(args, &param, &val);
18551e158c1SRusty Russell 		/* Stop at -- */
18651e158c1SRusty Russell 		if (!val && strcmp(param, "--") == 0)
18774b22c46SOleg Nesterov 			return err ?: args;
188a416aba6SArd van Breemen 		irq_was_disabled = irqs_disabled();
1899fb48c74SJim Cromie 		ret = parse_one(param, val, doing, params, num,
190ecc86170SLuis R. Rodriguez 				min_level, max_level, arg, unknown);
191b5f3abf9SJim Cromie 		if (irq_was_disabled && !irqs_disabled())
192b5f3abf9SJim Cromie 			pr_warn("%s: option '%s' enabled irq's!\n",
193b5f3abf9SJim Cromie 				doing, param);
194b5f3abf9SJim Cromie 
1951da177e4SLinus Torvalds 		switch (ret) {
19674b22c46SOleg Nesterov 		case 0:
19774b22c46SOleg Nesterov 			continue;
1981da177e4SLinus Torvalds 		case -ENOENT:
199b5f3abf9SJim Cromie 			pr_err("%s: Unknown parameter `%s'\n", doing, param);
20074b22c46SOleg Nesterov 			break;
2011da177e4SLinus Torvalds 		case -ENOSPC:
202b5f3abf9SJim Cromie 			pr_err("%s: `%s' too large for parameter `%s'\n",
2039fb48c74SJim Cromie 			       doing, val ?: "", param);
2041da177e4SLinus Torvalds 			break;
2051da177e4SLinus Torvalds 		default:
206b5f3abf9SJim Cromie 			pr_err("%s: `%s' invalid for parameter `%s'\n",
2079fb48c74SJim Cromie 			       doing, val ?: "", param);
20874b22c46SOleg Nesterov 			break;
2091da177e4SLinus Torvalds 		}
2101da177e4SLinus Torvalds 
21174b22c46SOleg Nesterov 		err = ERR_PTR(ret);
21274b22c46SOleg Nesterov 	}
21374b22c46SOleg Nesterov 
21474b22c46SOleg Nesterov 	return err;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Lazy bastard, eh? */
21888a88b32SFelipe Contreras #define STANDARD_PARAM_DEF(name, type, format, strtolfn)      		\
2199bbb9e5aSRusty Russell 	int param_set_##name(const char *val, const struct kernel_param *kp) \
2201da177e4SLinus Torvalds 	{								\
22188a88b32SFelipe Contreras 		return strtolfn(val, 0, (type *)kp->arg);		\
2221da177e4SLinus Torvalds 	}								\
2239bbb9e5aSRusty Russell 	int param_get_##name(char *buffer, const struct kernel_param *kp) \
2241da177e4SLinus Torvalds 	{								\
22596802e6bSJean Delvare 		return scnprintf(buffer, PAGE_SIZE, format "\n",	\
226f4940ab7SChen Gang 				*((type *)kp->arg));			\
227a14fe249SRusty Russell 	}								\
2289c27847dSLuis R. Rodriguez 	const struct kernel_param_ops param_ops_##name = {			\
2299bbb9e5aSRusty Russell 		.set = param_set_##name,				\
2309bbb9e5aSRusty Russell 		.get = param_get_##name,				\
2319bbb9e5aSRusty Russell 	};								\
232a14fe249SRusty Russell 	EXPORT_SYMBOL(param_set_##name);				\
2339bbb9e5aSRusty Russell 	EXPORT_SYMBOL(param_get_##name);				\
2349bbb9e5aSRusty Russell 	EXPORT_SYMBOL(param_ops_##name)
2359bbb9e5aSRusty Russell 
2361da177e4SLinus Torvalds 
23788a88b32SFelipe Contreras STANDARD_PARAM_DEF(byte,	unsigned char,		"%hhu",		kstrtou8);
23888a88b32SFelipe Contreras STANDARD_PARAM_DEF(short,	short,			"%hi",		kstrtos16);
23988a88b32SFelipe Contreras STANDARD_PARAM_DEF(ushort,	unsigned short,		"%hu",		kstrtou16);
24088a88b32SFelipe Contreras STANDARD_PARAM_DEF(int,		int,			"%i",		kstrtoint);
24188a88b32SFelipe Contreras STANDARD_PARAM_DEF(uint,	unsigned int,		"%u",		kstrtouint);
24288a88b32SFelipe Contreras STANDARD_PARAM_DEF(long,	long,			"%li",		kstrtol);
24388a88b32SFelipe Contreras STANDARD_PARAM_DEF(ulong,	unsigned long,		"%lu",		kstrtoul);
244b4210b81SHannes Reinecke STANDARD_PARAM_DEF(ullong,	unsigned long long,	"%llu",		kstrtoull);
2457d836577SPaul Menzel STANDARD_PARAM_DEF(hexint,	unsigned int,		"%#08x", 	kstrtouint);
2461da177e4SLinus Torvalds 
param_set_uint_minmax(const char * val,const struct kernel_param * kp,unsigned int min,unsigned int max)2472a14c9aeSSagi Grimberg int param_set_uint_minmax(const char *val, const struct kernel_param *kp,
2482a14c9aeSSagi Grimberg 		unsigned int min, unsigned int max)
2492a14c9aeSSagi Grimberg {
2502a14c9aeSSagi Grimberg 	unsigned int num;
2512a14c9aeSSagi Grimberg 	int ret;
2522a14c9aeSSagi Grimberg 
2532a14c9aeSSagi Grimberg 	if (!val)
2542a14c9aeSSagi Grimberg 		return -EINVAL;
2552a14c9aeSSagi Grimberg 	ret = kstrtouint(val, 0, &num);
2562a14c9aeSSagi Grimberg 	if (ret)
2572a14c9aeSSagi Grimberg 		return ret;
2582a14c9aeSSagi Grimberg 	if (num < min || num > max)
2592a14c9aeSSagi Grimberg 		return -EINVAL;
2602a14c9aeSSagi Grimberg 	*((unsigned int *)kp->arg) = num;
2612a14c9aeSSagi Grimberg 	return 0;
2622a14c9aeSSagi Grimberg }
2632a14c9aeSSagi Grimberg EXPORT_SYMBOL_GPL(param_set_uint_minmax);
2642a14c9aeSSagi Grimberg 
param_set_charp(const char * val,const struct kernel_param * kp)2659bbb9e5aSRusty Russell int param_set_charp(const char *val, const struct kernel_param *kp)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds 	if (strlen(val) > 1024) {
268b5f3abf9SJim Cromie 		pr_err("%s: string parameter too long\n", kp->name);
2691da177e4SLinus Torvalds 		return -ENOSPC;
2701da177e4SLinus Torvalds 	}
2711da177e4SLinus Torvalds 
272a1054322SRusty Russell 	maybe_kfree_parameter(*(char **)kp->arg);
273a1054322SRusty Russell 
274a1054322SRusty Russell 	/* This is a hack.  We can't kmalloc in early boot, and we
275e180a6b7SRusty Russell 	 * don't need to; this mangled commandline is preserved. */
276e180a6b7SRusty Russell 	if (slab_is_available()) {
277a1054322SRusty Russell 		*(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
278d553ad86SRusty Russell 		if (!*(char **)kp->arg)
279e180a6b7SRusty Russell 			return -ENOMEM;
280a1054322SRusty Russell 		strcpy(*(char **)kp->arg, val);
281e180a6b7SRusty Russell 	} else
282e180a6b7SRusty Russell 		*(const char **)kp->arg = val;
283e180a6b7SRusty Russell 
2841da177e4SLinus Torvalds 	return 0;
2851da177e4SLinus Torvalds }
286a14fe249SRusty Russell EXPORT_SYMBOL(param_set_charp);
2871da177e4SLinus Torvalds 
param_get_charp(char * buffer,const struct kernel_param * kp)2889bbb9e5aSRusty Russell int param_get_charp(char *buffer, const struct kernel_param *kp)
2891da177e4SLinus Torvalds {
29096802e6bSJean Delvare 	return scnprintf(buffer, PAGE_SIZE, "%s\n", *((char **)kp->arg));
2911da177e4SLinus Torvalds }
292a14fe249SRusty Russell EXPORT_SYMBOL(param_get_charp);
2931da177e4SLinus Torvalds 
param_free_charp(void * arg)2943d9c637fSDan Streetman void param_free_charp(void *arg)
295a1054322SRusty Russell {
296a1054322SRusty Russell 	maybe_kfree_parameter(*((char **)arg));
297a1054322SRusty Russell }
2983d9c637fSDan Streetman EXPORT_SYMBOL(param_free_charp);
299a1054322SRusty Russell 
3009c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_charp = {
3019bbb9e5aSRusty Russell 	.set = param_set_charp,
3029bbb9e5aSRusty Russell 	.get = param_get_charp,
303a1054322SRusty Russell 	.free = param_free_charp,
3049bbb9e5aSRusty Russell };
3059bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_charp);
3069bbb9e5aSRusty Russell 
307fddd5201SRusty Russell /* Actually could be a bool or an int, for historical reasons. */
param_set_bool(const char * val,const struct kernel_param * kp)3089bbb9e5aSRusty Russell int param_set_bool(const char *val, const struct kernel_param *kp)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	/* No equals means "set"... */
3111da177e4SLinus Torvalds 	if (!val) val = "1";
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	/* One of =[yYnN01] */
314def7b92eSChristophe JAILLET 	return kstrtobool(val, kp->arg);
315fddd5201SRusty Russell }
316a14fe249SRusty Russell EXPORT_SYMBOL(param_set_bool);
317fddd5201SRusty Russell 
param_get_bool(char * buffer,const struct kernel_param * kp)3189bbb9e5aSRusty Russell int param_get_bool(char *buffer, const struct kernel_param *kp)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	/* Y and N chosen as being relatively non-coder friendly */
32196802e6bSJean Delvare 	return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N');
3221da177e4SLinus Torvalds }
323a14fe249SRusty Russell EXPORT_SYMBOL(param_get_bool);
3241da177e4SLinus Torvalds 
3259c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bool = {
3266a4c2643SJani Nikula 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
3279bbb9e5aSRusty Russell 	.set = param_set_bool,
3289bbb9e5aSRusty Russell 	.get = param_get_bool,
3299bbb9e5aSRusty Russell };
3309bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_bool);
3319bbb9e5aSRusty Russell 
param_set_bool_enable_only(const char * val,const struct kernel_param * kp)332d19f05d8SLuis R. Rodriguez int param_set_bool_enable_only(const char *val, const struct kernel_param *kp)
333d19f05d8SLuis R. Rodriguez {
334*9ce170ceSLi zeming 	int err;
335d19f05d8SLuis R. Rodriguez 	bool new_value;
336d19f05d8SLuis R. Rodriguez 	bool orig_value = *(bool *)kp->arg;
337d19f05d8SLuis R. Rodriguez 	struct kernel_param dummy_kp = *kp;
338d19f05d8SLuis R. Rodriguez 
339d19f05d8SLuis R. Rodriguez 	dummy_kp.arg = &new_value;
340d19f05d8SLuis R. Rodriguez 
341d19f05d8SLuis R. Rodriguez 	err = param_set_bool(val, &dummy_kp);
342d19f05d8SLuis R. Rodriguez 	if (err)
343d19f05d8SLuis R. Rodriguez 		return err;
344d19f05d8SLuis R. Rodriguez 
345d19f05d8SLuis R. Rodriguez 	/* Don't let them unset it once it's set! */
346d19f05d8SLuis R. Rodriguez 	if (!new_value && orig_value)
347d19f05d8SLuis R. Rodriguez 		return -EROFS;
348d19f05d8SLuis R. Rodriguez 
349d19f05d8SLuis R. Rodriguez 	if (new_value)
350d19f05d8SLuis R. Rodriguez 		err = param_set_bool(val, kp);
351d19f05d8SLuis R. Rodriguez 
352d19f05d8SLuis R. Rodriguez 	return err;
353d19f05d8SLuis R. Rodriguez }
354d19f05d8SLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_set_bool_enable_only);
355d19f05d8SLuis R. Rodriguez 
356d19f05d8SLuis R. Rodriguez const struct kernel_param_ops param_ops_bool_enable_only = {
357d19f05d8SLuis R. Rodriguez 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
358d19f05d8SLuis R. Rodriguez 	.set = param_set_bool_enable_only,
359d19f05d8SLuis R. Rodriguez 	.get = param_get_bool,
360d19f05d8SLuis R. Rodriguez };
361154be21cSLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_ops_bool_enable_only);
362d19f05d8SLuis R. Rodriguez 
363fddd5201SRusty Russell /* This one must be bool. */
param_set_invbool(const char * val,const struct kernel_param * kp)3649bbb9e5aSRusty Russell int param_set_invbool(const char *val, const struct kernel_param *kp)
3651da177e4SLinus Torvalds {
366fddd5201SRusty Russell 	int ret;
367fddd5201SRusty Russell 	bool boolval;
36822e48eafSJan Beulich 	struct kernel_param dummy;
3691da177e4SLinus Torvalds 
37022e48eafSJan Beulich 	dummy.arg = &boolval;
3711da177e4SLinus Torvalds 	ret = param_set_bool(val, &dummy);
3721da177e4SLinus Torvalds 	if (ret == 0)
3739a71af2cSRusty Russell 		*(bool *)kp->arg = !boolval;
3741da177e4SLinus Torvalds 	return ret;
3751da177e4SLinus Torvalds }
376a14fe249SRusty Russell EXPORT_SYMBOL(param_set_invbool);
3771da177e4SLinus Torvalds 
param_get_invbool(char * buffer,const struct kernel_param * kp)3789bbb9e5aSRusty Russell int param_get_invbool(char *buffer, const struct kernel_param *kp)
3791da177e4SLinus Torvalds {
38096802e6bSJean Delvare 	return sprintf(buffer, "%c\n", (*(bool *)kp->arg) ? 'N' : 'Y');
3811da177e4SLinus Torvalds }
382a14fe249SRusty Russell EXPORT_SYMBOL(param_get_invbool);
3831da177e4SLinus Torvalds 
3849c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_invbool = {
3859bbb9e5aSRusty Russell 	.set = param_set_invbool,
3869bbb9e5aSRusty Russell 	.get = param_get_invbool,
3879bbb9e5aSRusty Russell };
3889bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_invbool);
3899bbb9e5aSRusty Russell 
param_set_bint(const char * val,const struct kernel_param * kp)39069116f27SRusty Russell int param_set_bint(const char *val, const struct kernel_param *kp)
39169116f27SRusty Russell {
3925104b7d7SDan Streetman 	/* Match bool exactly, by re-using it. */
3935104b7d7SDan Streetman 	struct kernel_param boolkp = *kp;
39469116f27SRusty Russell 	bool v;
39569116f27SRusty Russell 	int ret;
39669116f27SRusty Russell 
39769116f27SRusty Russell 	boolkp.arg = &v;
39869116f27SRusty Russell 
39969116f27SRusty Russell 	ret = param_set_bool(val, &boolkp);
40069116f27SRusty Russell 	if (ret == 0)
40169116f27SRusty Russell 		*(int *)kp->arg = v;
40269116f27SRusty Russell 	return ret;
40369116f27SRusty Russell }
40469116f27SRusty Russell EXPORT_SYMBOL(param_set_bint);
40569116f27SRusty Russell 
4069c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bint = {
4076a4c2643SJani Nikula 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
40869116f27SRusty Russell 	.set = param_set_bint,
40969116f27SRusty Russell 	.get = param_get_int,
41069116f27SRusty Russell };
41169116f27SRusty Russell EXPORT_SYMBOL(param_ops_bint);
41269116f27SRusty Russell 
4139730b5b0SBert Wesarg /* We break the rule and mangle the string. */
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)414b51d23e4SDan Streetman static int param_array(struct module *mod,
415b51d23e4SDan Streetman 		       const char *name,
4161da177e4SLinus Torvalds 		       const char *val,
4171da177e4SLinus Torvalds 		       unsigned int min, unsigned int max,
4181da177e4SLinus Torvalds 		       void *elem, int elemsize,
4199bbb9e5aSRusty Russell 		       int (*set)(const char *, const struct kernel_param *kp),
420026cee00SPawel Moll 		       s16 level,
421eb38a996SRichard Knutsson 		       unsigned int *num)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds 	int ret;
4241da177e4SLinus Torvalds 	struct kernel_param kp;
4251da177e4SLinus Torvalds 	char save;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	/* Get the name right for errors. */
4281da177e4SLinus Torvalds 	kp.name = name;
4291da177e4SLinus Torvalds 	kp.arg = elem;
430026cee00SPawel Moll 	kp.level = level;
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	*num = 0;
4331da177e4SLinus Torvalds 	/* We expect a comma-separated list of values. */
4341da177e4SLinus Torvalds 	do {
4351da177e4SLinus Torvalds 		int len;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 		if (*num == max) {
438b5f3abf9SJim Cromie 			pr_err("%s: can only take %i arguments\n", name, max);
4391da177e4SLinus Torvalds 			return -EINVAL;
4401da177e4SLinus Torvalds 		}
4411da177e4SLinus Torvalds 		len = strcspn(val, ",");
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 		/* nul-terminate and parse */
4441da177e4SLinus Torvalds 		save = val[len];
4451da177e4SLinus Torvalds 		((char *)val)[len] = '\0';
446cf2fde7bSRusty Russell 		check_kparam_locked(mod);
4471da177e4SLinus Torvalds 		ret = set(val, &kp);
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 		if (ret != 0)
4501da177e4SLinus Torvalds 			return ret;
4511da177e4SLinus Torvalds 		kp.arg += elemsize;
4521da177e4SLinus Torvalds 		val += len+1;
4531da177e4SLinus Torvalds 		(*num)++;
4541da177e4SLinus Torvalds 	} while (save == ',');
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	if (*num < min) {
457b5f3abf9SJim Cromie 		pr_err("%s: needs at least %i arguments\n", name, min);
4581da177e4SLinus Torvalds 		return -EINVAL;
4591da177e4SLinus Torvalds 	}
4601da177e4SLinus Torvalds 	return 0;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds 
param_array_set(const char * val,const struct kernel_param * kp)4639bbb9e5aSRusty Russell static int param_array_set(const char *val, const struct kernel_param *kp)
4641da177e4SLinus Torvalds {
46522e48eafSJan Beulich 	const struct kparam_array *arr = kp->arr;
46631143a12SBert Wesarg 	unsigned int temp_num;
4671da177e4SLinus Torvalds 
468b51d23e4SDan Streetman 	return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
469026cee00SPawel Moll 			   arr->elemsize, arr->ops->set, kp->level,
4703c7d76e3SRusty Russell 			   arr->num ?: &temp_num);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
param_array_get(char * buffer,const struct kernel_param * kp)4739bbb9e5aSRusty Russell static int param_array_get(char *buffer, const struct kernel_param *kp)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds 	int i, off, ret;
47622e48eafSJan Beulich 	const struct kparam_array *arr = kp->arr;
4775104b7d7SDan Streetman 	struct kernel_param p = *kp;
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
48096802e6bSJean Delvare 		/* Replace \n with comma */
4811da177e4SLinus Torvalds 		if (i)
48296802e6bSJean Delvare 			buffer[off - 1] = ',';
4831da177e4SLinus Torvalds 		p.arg = arr->elem + arr->elemsize * i;
484cf2fde7bSRusty Russell 		check_kparam_locked(p.mod);
4859bbb9e5aSRusty Russell 		ret = arr->ops->get(buffer + off, &p);
4861da177e4SLinus Torvalds 		if (ret < 0)
4871da177e4SLinus Torvalds 			return ret;
4881da177e4SLinus Torvalds 		off += ret;
4891da177e4SLinus Torvalds 	}
4901da177e4SLinus Torvalds 	buffer[off] = '\0';
4911da177e4SLinus Torvalds 	return off;
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds 
param_array_free(void * arg)494e6df34a4SRusty Russell static void param_array_free(void *arg)
495e6df34a4SRusty Russell {
496e6df34a4SRusty Russell 	unsigned int i;
497e6df34a4SRusty Russell 	const struct kparam_array *arr = arg;
498e6df34a4SRusty Russell 
499e6df34a4SRusty Russell 	if (arr->ops->free)
500e6df34a4SRusty Russell 		for (i = 0; i < (arr->num ? *arr->num : arr->max); i++)
501e6df34a4SRusty Russell 			arr->ops->free(arr->elem + arr->elemsize * i);
502e6df34a4SRusty Russell }
503e6df34a4SRusty Russell 
5049c27847dSLuis R. Rodriguez const struct kernel_param_ops param_array_ops = {
5059bbb9e5aSRusty Russell 	.set = param_array_set,
5069bbb9e5aSRusty Russell 	.get = param_array_get,
507e6df34a4SRusty Russell 	.free = param_array_free,
5089bbb9e5aSRusty Russell };
5099bbb9e5aSRusty Russell EXPORT_SYMBOL(param_array_ops);
5109bbb9e5aSRusty Russell 
param_set_copystring(const char * val,const struct kernel_param * kp)5119bbb9e5aSRusty Russell int param_set_copystring(const char *val, const struct kernel_param *kp)
5121da177e4SLinus Torvalds {
51322e48eafSJan Beulich 	const struct kparam_string *kps = kp->str;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 	if (strlen(val)+1 > kps->maxlen) {
516b5f3abf9SJim Cromie 		pr_err("%s: string doesn't fit in %u chars.\n",
5171da177e4SLinus Torvalds 		       kp->name, kps->maxlen-1);
5181da177e4SLinus Torvalds 		return -ENOSPC;
5191da177e4SLinus Torvalds 	}
5201da177e4SLinus Torvalds 	strcpy(kps->string, val);
5211da177e4SLinus Torvalds 	return 0;
5221da177e4SLinus Torvalds }
523a14fe249SRusty Russell EXPORT_SYMBOL(param_set_copystring);
5241da177e4SLinus Torvalds 
param_get_string(char * buffer,const struct kernel_param * kp)5259bbb9e5aSRusty Russell int param_get_string(char *buffer, const struct kernel_param *kp)
5261da177e4SLinus Torvalds {
52722e48eafSJan Beulich 	const struct kparam_string *kps = kp->str;
52896802e6bSJean Delvare 	return scnprintf(buffer, PAGE_SIZE, "%s\n", kps->string);
5291da177e4SLinus Torvalds }
530a14fe249SRusty Russell EXPORT_SYMBOL(param_get_string);
5311da177e4SLinus Torvalds 
5329c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_string = {
5339bbb9e5aSRusty Russell 	.set = param_set_copystring,
5349bbb9e5aSRusty Russell 	.get = param_get_string,
5359bbb9e5aSRusty Russell };
5369bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_string);
5379bbb9e5aSRusty Russell 
5381da177e4SLinus Torvalds /* sysfs output in /sys/modules/XYZ/parameters/ */
539350f8258SEdward Z. Yang #define to_module_attr(n) container_of(n, struct module_attribute, attr)
540350f8258SEdward Z. Yang #define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds struct param_attribute
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds 	struct module_attribute mattr;
5459bbb9e5aSRusty Russell 	const struct kernel_param *param;
5461da177e4SLinus Torvalds };
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds struct module_param_attrs
5491da177e4SLinus Torvalds {
5509b473de8SRusty Russell 	unsigned int num;
5511da177e4SLinus Torvalds 	struct attribute_group grp;
552fa29c9c1SGustavo A. R. Silva 	struct param_attribute attrs[];
5531da177e4SLinus Torvalds };
5541da177e4SLinus Torvalds 
555ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
556350f8258SEdward Z. Yang #define to_param_attr(n) container_of(n, struct param_attribute, mattr)
5571da177e4SLinus Torvalds 
param_attr_show(struct module_attribute * mattr,struct module_kobject * mk,char * buf)5581da177e4SLinus Torvalds static ssize_t param_attr_show(struct module_attribute *mattr,
5594befb026SKay Sievers 			       struct module_kobject *mk, char *buf)
5601da177e4SLinus Torvalds {
5611da177e4SLinus Torvalds 	int count;
5621da177e4SLinus Torvalds 	struct param_attribute *attribute = to_param_attr(mattr);
5631da177e4SLinus Torvalds 
5649bbb9e5aSRusty Russell 	if (!attribute->param->ops->get)
5651da177e4SLinus Torvalds 		return -EPERM;
5661da177e4SLinus Torvalds 
567b51d23e4SDan Streetman 	kernel_param_lock(mk->mod);
5689bbb9e5aSRusty Russell 	count = attribute->param->ops->get(buf, attribute->param);
569b51d23e4SDan Streetman 	kernel_param_unlock(mk->mod);
5701da177e4SLinus Torvalds 	return count;
5711da177e4SLinus Torvalds }
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds /* sysfs always hands a nul-terminated string in buf.  We rely on that. */
param_attr_store(struct module_attribute * mattr,struct module_kobject * mk,const char * buf,size_t len)5741da177e4SLinus Torvalds static ssize_t param_attr_store(struct module_attribute *mattr,
575b51d23e4SDan Streetman 				struct module_kobject *mk,
5761da177e4SLinus Torvalds 				const char *buf, size_t len)
5771da177e4SLinus Torvalds {
5781da177e4SLinus Torvalds  	int err;
5791da177e4SLinus Torvalds 	struct param_attribute *attribute = to_param_attr(mattr);
5801da177e4SLinus Torvalds 
5819bbb9e5aSRusty Russell 	if (!attribute->param->ops->set)
5821da177e4SLinus Torvalds 		return -EPERM;
5831da177e4SLinus Torvalds 
584b51d23e4SDan Streetman 	kernel_param_lock(mk->mod);
58520657f66SDavid Howells 	if (param_check_unsafe(attribute->param))
5869bbb9e5aSRusty Russell 		err = attribute->param->ops->set(buf, attribute->param);
58720657f66SDavid Howells 	else
58820657f66SDavid Howells 		err = -EPERM;
589b51d23e4SDan Streetman 	kernel_param_unlock(mk->mod);
5901da177e4SLinus Torvalds 	if (!err)
5911da177e4SLinus Torvalds 		return len;
5921da177e4SLinus Torvalds 	return err;
5931da177e4SLinus Torvalds }
594ef665c1aSRandy Dunlap #endif
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds #ifdef CONFIG_MODULES
5971da177e4SLinus Torvalds #define __modinit
5981da177e4SLinus Torvalds #else
5991da177e4SLinus Torvalds #define __modinit __init
6001da177e4SLinus Torvalds #endif
6011da177e4SLinus Torvalds 
602ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
kernel_param_lock(struct module * mod)603b51d23e4SDan Streetman void kernel_param_lock(struct module *mod)
604907b29ebSRusty Russell {
605b51d23e4SDan Streetman 	mutex_lock(KPARAM_MUTEX(mod));
606907b29ebSRusty Russell }
607907b29ebSRusty Russell 
kernel_param_unlock(struct module * mod)608b51d23e4SDan Streetman void kernel_param_unlock(struct module *mod)
609907b29ebSRusty Russell {
610b51d23e4SDan Streetman 	mutex_unlock(KPARAM_MUTEX(mod));
611907b29ebSRusty Russell }
612b51d23e4SDan Streetman 
613b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_lock);
614b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_unlock);
615907b29ebSRusty Russell 
6161da177e4SLinus Torvalds /*
6179b473de8SRusty Russell  * add_sysfs_param - add a parameter to sysfs
6189b473de8SRusty Russell  * @mk: struct module_kobject
619630cc2b3SJean Delvare  * @kp: the actual parameter definition to add to sysfs
6209b473de8SRusty Russell  * @name: name of parameter
6211da177e4SLinus Torvalds  *
6229b473de8SRusty Russell  * Create a kobject if for a (per-module) parameter if mp NULL, and
6239b473de8SRusty Russell  * create file in sysfs.  Returns an error on out of memory.  Always cleans up
6249b473de8SRusty Russell  * if there's an error.
6251da177e4SLinus Torvalds  */
add_sysfs_param(struct module_kobject * mk,const struct kernel_param * kp,const char * name)6269b473de8SRusty Russell static __modinit int add_sysfs_param(struct module_kobject *mk,
6279bbb9e5aSRusty Russell 				     const struct kernel_param *kp,
6289b473de8SRusty Russell 				     const char *name)
6291da177e4SLinus Torvalds {
63018eb74faSRusty Russell 	struct module_param_attrs *new_mp;
63118eb74faSRusty Russell 	struct attribute **new_attrs;
63218eb74faSRusty Russell 	unsigned int i;
6331da177e4SLinus Torvalds 
6349b473de8SRusty Russell 	/* We don't bother calling this with invisible parameters. */
6359b473de8SRusty Russell 	BUG_ON(!kp->perm);
6369b473de8SRusty Russell 
6379b473de8SRusty Russell 	if (!mk->mp) {
63818eb74faSRusty Russell 		/* First allocation. */
63918eb74faSRusty Russell 		mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
64018eb74faSRusty Russell 		if (!mk->mp)
64118eb74faSRusty Russell 			return -ENOMEM;
64218eb74faSRusty Russell 		mk->mp->grp.name = "parameters";
64318eb74faSRusty Russell 		/* NULL-terminated attribute array. */
64418eb74faSRusty Russell 		mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
6459b473de8SRusty Russell 					    GFP_KERNEL);
64618eb74faSRusty Russell 		/* Caller will cleanup via free_module_param_attrs */
64718eb74faSRusty Russell 		if (!mk->mp->grp.attrs)
64818eb74faSRusty Russell 			return -ENOMEM;
6491da177e4SLinus Torvalds 	}
6501da177e4SLinus Torvalds 
65118eb74faSRusty Russell 	/* Enlarge allocations. */
65218eb74faSRusty Russell 	new_mp = krealloc(mk->mp,
65318eb74faSRusty Russell 			  sizeof(*mk->mp) +
65418eb74faSRusty Russell 			  sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
65518eb74faSRusty Russell 			  GFP_KERNEL);
65618eb74faSRusty Russell 	if (!new_mp)
65718eb74faSRusty Russell 		return -ENOMEM;
65818eb74faSRusty Russell 	mk->mp = new_mp;
65918eb74faSRusty Russell 
66018eb74faSRusty Russell 	/* Extra pointer for NULL terminator */
66118eb74faSRusty Russell 	new_attrs = krealloc(mk->mp->grp.attrs,
66218eb74faSRusty Russell 			     sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
66318eb74faSRusty Russell 			     GFP_KERNEL);
66418eb74faSRusty Russell 	if (!new_attrs)
66518eb74faSRusty Russell 		return -ENOMEM;
66618eb74faSRusty Russell 	mk->mp->grp.attrs = new_attrs;
6679b473de8SRusty Russell 
6689b473de8SRusty Russell 	/* Tack new one on the end. */
669c772be52SRusty Russell 	memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0]));
67018eb74faSRusty Russell 	sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
67118eb74faSRusty Russell 	mk->mp->attrs[mk->mp->num].param = kp;
67218eb74faSRusty Russell 	mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
673b0a65b0cSKees Cook 	/* Do not allow runtime DAC changes to make param writable. */
674b0a65b0cSKees Cook 	if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
67518eb74faSRusty Russell 		mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
676574732c7SRusty Russell 	else
677574732c7SRusty Russell 		mk->mp->attrs[mk->mp->num].mattr.store = NULL;
67818eb74faSRusty Russell 	mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
67918eb74faSRusty Russell 	mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
68018eb74faSRusty Russell 	mk->mp->num++;
6819b473de8SRusty Russell 
6829b473de8SRusty Russell 	/* Fix up all the pointers, since krealloc can move us */
68318eb74faSRusty Russell 	for (i = 0; i < mk->mp->num; i++)
68418eb74faSRusty Russell 		mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
68518eb74faSRusty Russell 	mk->mp->grp.attrs[mk->mp->num] = NULL;
6869b473de8SRusty Russell 	return 0;
6871da177e4SLinus Torvalds }
6889b473de8SRusty Russell 
689d2441183SLinus Torvalds #ifdef CONFIG_MODULES
free_module_param_attrs(struct module_kobject * mk)6909b473de8SRusty Russell static void free_module_param_attrs(struct module_kobject *mk)
6919b473de8SRusty Russell {
69218eb74faSRusty Russell 	if (mk->mp)
6939b473de8SRusty Russell 		kfree(mk->mp->grp.attrs);
6949b473de8SRusty Russell 	kfree(mk->mp);
6959b473de8SRusty Russell 	mk->mp = NULL;
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds /*
6991da177e4SLinus Torvalds  * module_param_sysfs_setup - setup sysfs support for one module
7001da177e4SLinus Torvalds  * @mod: module
7011da177e4SLinus Torvalds  * @kparam: module parameters (array)
7021da177e4SLinus Torvalds  * @num_params: number of module parameters
7031da177e4SLinus Torvalds  *
7049b473de8SRusty Russell  * Adds sysfs entries for module parameters under
7059b473de8SRusty Russell  * /sys/module/[mod->name]/parameters/
7061da177e4SLinus Torvalds  */
module_param_sysfs_setup(struct module * mod,const struct kernel_param * kparam,unsigned int num_params)7071da177e4SLinus Torvalds int module_param_sysfs_setup(struct module *mod,
7089bbb9e5aSRusty Russell 			     const struct kernel_param *kparam,
7091da177e4SLinus Torvalds 			     unsigned int num_params)
7101da177e4SLinus Torvalds {
7119b473de8SRusty Russell 	int i, err;
7129b473de8SRusty Russell 	bool params = false;
7131da177e4SLinus Torvalds 
7149b473de8SRusty Russell 	for (i = 0; i < num_params; i++) {
7159b473de8SRusty Russell 		if (kparam[i].perm == 0)
7169b473de8SRusty Russell 			continue;
7179b473de8SRusty Russell 		err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
71818eb74faSRusty Russell 		if (err) {
71918eb74faSRusty Russell 			free_module_param_attrs(&mod->mkobj);
7209b473de8SRusty Russell 			return err;
72118eb74faSRusty Russell 		}
7229b473de8SRusty Russell 		params = true;
7239b473de8SRusty Russell 	}
7241da177e4SLinus Torvalds 
7259b473de8SRusty Russell 	if (!params)
7261da177e4SLinus Torvalds 		return 0;
7279b473de8SRusty Russell 
7289b473de8SRusty Russell 	/* Create the param group. */
7299b473de8SRusty Russell 	err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
7309b473de8SRusty Russell 	if (err)
7319b473de8SRusty Russell 		free_module_param_attrs(&mod->mkobj);
7329b473de8SRusty Russell 	return err;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds /*
7361da177e4SLinus Torvalds  * module_param_sysfs_remove - remove sysfs support for one module
7371da177e4SLinus Torvalds  * @mod: module
7381da177e4SLinus Torvalds  *
7391da177e4SLinus Torvalds  * Remove sysfs entries for module parameters and the corresponding
7401da177e4SLinus Torvalds  * kobject.
7411da177e4SLinus Torvalds  */
module_param_sysfs_remove(struct module * mod)7421da177e4SLinus Torvalds void module_param_sysfs_remove(struct module *mod)
7431da177e4SLinus Torvalds {
7449b473de8SRusty Russell 	if (mod->mkobj.mp) {
7459b473de8SRusty Russell 		sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
7461da177e4SLinus Torvalds 		/* We are positive that no one is using any param
7471da177e4SLinus Torvalds 		 * attrs at this point.  Deallocate immediately. */
7489b473de8SRusty Russell 		free_module_param_attrs(&mod->mkobj);
7491da177e4SLinus Torvalds 	}
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds #endif
7521da177e4SLinus Torvalds 
destroy_params(const struct kernel_param * params,unsigned num)753e180a6b7SRusty Russell void destroy_params(const struct kernel_param *params, unsigned num)
754e180a6b7SRusty Russell {
755e6df34a4SRusty Russell 	unsigned int i;
756e6df34a4SRusty Russell 
757e6df34a4SRusty Russell 	for (i = 0; i < num; i++)
758e6df34a4SRusty Russell 		if (params[i].ops->free)
759e6df34a4SRusty Russell 			params[i].ops->free(params[i].arg);
760e180a6b7SRusty Russell }
761e180a6b7SRusty Russell 
locate_module_kobject(const char * name)762e94965edSDmitry Torokhov static struct module_kobject * __init locate_module_kobject(const char *name)
7631da177e4SLinus Torvalds {
7641da177e4SLinus Torvalds 	struct module_kobject *mk;
7659b473de8SRusty Russell 	struct kobject *kobj;
7669b473de8SRusty Russell 	int err;
7671da177e4SLinus Torvalds 
7689b473de8SRusty Russell 	kobj = kset_find_obj(module_kset, name);
7699b473de8SRusty Russell 	if (kobj) {
7709b473de8SRusty Russell 		mk = to_module_kobject(kobj);
7719b473de8SRusty Russell 	} else {
772dd392710SPekka J Enberg 		mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
773dd392710SPekka J Enberg 		BUG_ON(!mk);
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 		mk->mod = THIS_MODULE;
7767405c1e1SGreg Kroah-Hartman 		mk->kobj.kset = module_kset;
7779b473de8SRusty Russell 		err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,
7789b473de8SRusty Russell 					   "%s", name);
77988bfa324SKay Sievers #ifdef CONFIG_MODULES
78088bfa324SKay Sievers 		if (!err)
78188bfa324SKay Sievers 			err = sysfs_create_file(&mk->kobj, &module_uevent.attr);
78288bfa324SKay Sievers #endif
7839b473de8SRusty Russell 		if (err) {
784e43b9192SGreg Kroah-Hartman 			kobject_put(&mk->kobj);
785b5f3abf9SJim Cromie 			pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",
786e94965edSDmitry Torokhov 				name, err);
787e94965edSDmitry Torokhov 			return NULL;
78874c5b597SGreg Kroah-Hartman 		}
789e94965edSDmitry Torokhov 
790e94965edSDmitry Torokhov 		/* So that we hold reference in both cases. */
7919b473de8SRusty Russell 		kobject_get(&mk->kobj);
7929b473de8SRusty Russell 	}
7939b473de8SRusty Russell 
794e94965edSDmitry Torokhov 	return mk;
795e94965edSDmitry Torokhov }
796e94965edSDmitry Torokhov 
kernel_add_sysfs_param(const char * name,const struct kernel_param * kparam,unsigned int name_skip)797e94965edSDmitry Torokhov static void __init kernel_add_sysfs_param(const char *name,
79863a12d9dSGeert Uytterhoeven 					  const struct kernel_param *kparam,
799e94965edSDmitry Torokhov 					  unsigned int name_skip)
800e94965edSDmitry Torokhov {
801e94965edSDmitry Torokhov 	struct module_kobject *mk;
802e94965edSDmitry Torokhov 	int err;
803e94965edSDmitry Torokhov 
804e94965edSDmitry Torokhov 	mk = locate_module_kobject(name);
805e94965edSDmitry Torokhov 	if (!mk)
806e94965edSDmitry Torokhov 		return;
807e94965edSDmitry Torokhov 
808e94965edSDmitry Torokhov 	/* We need to remove old parameters before adding more. */
809e94965edSDmitry Torokhov 	if (mk->mp)
810e94965edSDmitry Torokhov 		sysfs_remove_group(&mk->kobj, &mk->mp->grp);
811e94965edSDmitry Torokhov 
8129b473de8SRusty Russell 	/* These should not fail at boot. */
8139b473de8SRusty Russell 	err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
8149b473de8SRusty Russell 	BUG_ON(err);
8159b473de8SRusty Russell 	err = sysfs_create_group(&mk->kobj, &mk->mp->grp);
8169b473de8SRusty Russell 	BUG_ON(err);
817f30c53a8SKay Sievers 	kobject_uevent(&mk->kobj, KOBJ_ADD);
8189b473de8SRusty Russell 	kobject_put(&mk->kobj);
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds /*
822b634d130SJean Delvare  * param_sysfs_builtin - add sysfs parameters for built-in modules
8231da177e4SLinus Torvalds  *
8241da177e4SLinus Torvalds  * Add module_parameters to sysfs for "modules" built into the kernel.
8251da177e4SLinus Torvalds  *
8261da177e4SLinus Torvalds  * The "module" name (KBUILD_MODNAME) is stored before a dot, the
8271da177e4SLinus Torvalds  * "parameter" name is stored behind a dot in kernel_param->name. So,
8281da177e4SLinus Torvalds  * extract the "module" name for all built-in kernel_param-eters,
8299b473de8SRusty Russell  * and for all who have the same, call kernel_add_sysfs_param.
8301da177e4SLinus Torvalds  */
param_sysfs_builtin(void)8311da177e4SLinus Torvalds static void __init param_sysfs_builtin(void)
8321da177e4SLinus Torvalds {
83363a12d9dSGeert Uytterhoeven 	const struct kernel_param *kp;
8349b473de8SRusty Russell 	unsigned int name_len;
8359b473de8SRusty Russell 	char modname[MODULE_NAME_LEN];
8361da177e4SLinus Torvalds 
8379b473de8SRusty Russell 	for (kp = __start___param; kp < __stop___param; kp++) {
8381da177e4SLinus Torvalds 		char *dot;
8391da177e4SLinus Torvalds 
8409b473de8SRusty Russell 		if (kp->perm == 0)
8419b473de8SRusty Russell 			continue;
8421da177e4SLinus Torvalds 
843730b69d2SRusty Russell 		dot = strchr(kp->name, '.');
8441da177e4SLinus Torvalds 		if (!dot) {
84567e67ceaSRusty Russell 			/* This happens for core_param() */
84667e67ceaSRusty Russell 			strcpy(modname, "kernel");
84767e67ceaSRusty Russell 			name_len = 0;
84867e67ceaSRusty Russell 		} else {
84967e67ceaSRusty Russell 			name_len = dot - kp->name + 1;
85033457938SAzeem Shaikh 			strscpy(modname, kp->name, name_len);
8511da177e4SLinus Torvalds 		}
85267e67ceaSRusty Russell 		kernel_add_sysfs_param(modname, kp, name_len);
8531da177e4SLinus Torvalds 	}
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds 
__modver_version_show(struct module_attribute * mattr,struct module_kobject * mk,char * buf)856e94965edSDmitry Torokhov ssize_t __modver_version_show(struct module_attribute *mattr,
8574befb026SKay Sievers 			      struct module_kobject *mk, char *buf)
858e94965edSDmitry Torokhov {
859e94965edSDmitry Torokhov 	struct module_version_attribute *vattr =
860e94965edSDmitry Torokhov 		container_of(mattr, struct module_version_attribute, mattr);
861e94965edSDmitry Torokhov 
862f4940ab7SChen Gang 	return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version);
863e94965edSDmitry Torokhov }
864e94965edSDmitry Torokhov 
865b112082cSJohan Hovold extern const struct module_version_attribute __start___modver[];
866b112082cSJohan Hovold extern const struct module_version_attribute __stop___modver[];
867e94965edSDmitry Torokhov 
version_sysfs_builtin(void)868e94965edSDmitry Torokhov static void __init version_sysfs_builtin(void)
869e94965edSDmitry Torokhov {
870b112082cSJohan Hovold 	const struct module_version_attribute *vattr;
871e94965edSDmitry Torokhov 	struct module_kobject *mk;
872e94965edSDmitry Torokhov 	int err;
873e94965edSDmitry Torokhov 
874b112082cSJohan Hovold 	for (vattr = __start___modver; vattr < __stop___modver; vattr++) {
875e94965edSDmitry Torokhov 		mk = locate_module_kobject(vattr->module_name);
876e94965edSDmitry Torokhov 		if (mk) {
877e94965edSDmitry Torokhov 			err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
87874c3dea3SRusty Russell 			WARN_ON_ONCE(err);
879e94965edSDmitry Torokhov 			kobject_uevent(&mk->kobj, KOBJ_ADD);
880e94965edSDmitry Torokhov 			kobject_put(&mk->kobj);
881e94965edSDmitry Torokhov 		}
882e94965edSDmitry Torokhov 	}
883e94965edSDmitry Torokhov }
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds /* module-related sysfs stuff */
8861da177e4SLinus Torvalds 
module_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)8871da177e4SLinus Torvalds static ssize_t module_attr_show(struct kobject *kobj,
8881da177e4SLinus Torvalds 				struct attribute *attr,
8891da177e4SLinus Torvalds 				char *buf)
8901da177e4SLinus Torvalds {
8911da177e4SLinus Torvalds 	struct module_attribute *attribute;
8921da177e4SLinus Torvalds 	struct module_kobject *mk;
8931da177e4SLinus Torvalds 	int ret;
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	attribute = to_module_attr(attr);
8961da177e4SLinus Torvalds 	mk = to_module_kobject(kobj);
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds 	if (!attribute->show)
89970f2817aSDmitry Torokhov 		return -EIO;
9001da177e4SLinus Torvalds 
9014befb026SKay Sievers 	ret = attribute->show(attribute, mk, buf);
9021da177e4SLinus Torvalds 
9031da177e4SLinus Torvalds 	return ret;
9041da177e4SLinus Torvalds }
9051da177e4SLinus Torvalds 
module_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t len)9061da177e4SLinus Torvalds static ssize_t module_attr_store(struct kobject *kobj,
9071da177e4SLinus Torvalds 				struct attribute *attr,
9081da177e4SLinus Torvalds 				const char *buf, size_t len)
9091da177e4SLinus Torvalds {
9101da177e4SLinus Torvalds 	struct module_attribute *attribute;
9111da177e4SLinus Torvalds 	struct module_kobject *mk;
9121da177e4SLinus Torvalds 	int ret;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	attribute = to_module_attr(attr);
9151da177e4SLinus Torvalds 	mk = to_module_kobject(kobj);
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	if (!attribute->store)
91870f2817aSDmitry Torokhov 		return -EIO;
9191da177e4SLinus Torvalds 
9204befb026SKay Sievers 	ret = attribute->store(attribute, mk, buf, len);
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds 	return ret;
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds 
92552cf25d0SEmese Revfy static const struct sysfs_ops module_sysfs_ops = {
9261da177e4SLinus Torvalds 	.show = module_attr_show,
9271da177e4SLinus Torvalds 	.store = module_attr_store,
9281da177e4SLinus Torvalds };
9291da177e4SLinus Torvalds 
uevent_filter(const struct kobject * kobj)930c45a88bbSGreg Kroah-Hartman static int uevent_filter(const struct kobject *kobj)
931270a6c4cSKay Sievers {
932ee6d3dd4SWedson Almeida Filho 	const struct kobj_type *ktype = get_ktype(kobj);
933270a6c4cSKay Sievers 
934270a6c4cSKay Sievers 	if (ktype == &module_ktype)
935270a6c4cSKay Sievers 		return 1;
936270a6c4cSKay Sievers 	return 0;
937270a6c4cSKay Sievers }
938270a6c4cSKay Sievers 
9399cd43611SEmese Revfy static const struct kset_uevent_ops module_uevent_ops = {
940270a6c4cSKay Sievers 	.filter = uevent_filter,
941270a6c4cSKay Sievers };
942270a6c4cSKay Sievers 
9437405c1e1SGreg Kroah-Hartman struct kset *module_kset;
9441da177e4SLinus Torvalds 
module_kobj_release(struct kobject * kobj)945942e4431SLi Zhong static void module_kobj_release(struct kobject *kobj)
946942e4431SLi Zhong {
947942e4431SLi Zhong 	struct module_kobject *mk = to_module_kobject(kobj);
948942e4431SLi Zhong 	complete(mk->kobj_completion);
949942e4431SLi Zhong }
950942e4431SLi Zhong 
951042edf1eSThomas Weißschuh const struct kobj_type module_ktype = {
952942e4431SLi Zhong 	.release   =	module_kobj_release,
9531da177e4SLinus Torvalds 	.sysfs_ops =	&module_sysfs_ops,
9541da177e4SLinus Torvalds };
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds /*
95796a1a241SRasmus Villemoes  * param_sysfs_init - create "module" kset
95896a1a241SRasmus Villemoes  *
95996a1a241SRasmus Villemoes  * This must be done before the initramfs is unpacked and
96096a1a241SRasmus Villemoes  * request_module() thus becomes possible, because otherwise the
96196a1a241SRasmus Villemoes  * module load would fail in mod_sysfs_init.
9621da177e4SLinus Torvalds  */
param_sysfs_init(void)9631da177e4SLinus Torvalds static int __init param_sysfs_init(void)
9641da177e4SLinus Torvalds {
9657405c1e1SGreg Kroah-Hartman 	module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);
9667405c1e1SGreg Kroah-Hartman 	if (!module_kset) {
9677405c1e1SGreg Kroah-Hartman 		printk(KERN_WARNING "%s (%d): error creating kset\n",
9687405c1e1SGreg Kroah-Hartman 			__FILE__, __LINE__);
9697405c1e1SGreg Kroah-Hartman 		return -ENOMEM;
970d8c7649eSRandy Dunlap 	}
9711da177e4SLinus Torvalds 
97296a1a241SRasmus Villemoes 	return 0;
97396a1a241SRasmus Villemoes }
97496a1a241SRasmus Villemoes subsys_initcall(param_sysfs_init);
97596a1a241SRasmus Villemoes 
97696a1a241SRasmus Villemoes /*
97796a1a241SRasmus Villemoes  * param_sysfs_builtin_init - add sysfs version and parameter
97896a1a241SRasmus Villemoes  * attributes for built-in modules
97996a1a241SRasmus Villemoes  */
param_sysfs_builtin_init(void)98096a1a241SRasmus Villemoes static int __init param_sysfs_builtin_init(void)
98196a1a241SRasmus Villemoes {
98296a1a241SRasmus Villemoes 	if (!module_kset)
98396a1a241SRasmus Villemoes 		return -ENOMEM;
98496a1a241SRasmus Villemoes 
985e94965edSDmitry Torokhov 	version_sysfs_builtin();
9861da177e4SLinus Torvalds 	param_sysfs_builtin();
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds 	return 0;
9891da177e4SLinus Torvalds }
99096a1a241SRasmus Villemoes late_initcall(param_sysfs_builtin_init);
9911da177e4SLinus Torvalds 
9927405c1e1SGreg Kroah-Hartman #endif /* CONFIG_SYSFS */
993