xref: /linux-6.15/kernel/params.c (revision a6aeb739)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b5e3f86aSAndy Shevchenko /*
3b5e3f86aSAndy Shevchenko  * Helpers for initial module or kernel cmdline parsing
4b5e3f86aSAndy Shevchenko  * Copyright (C) 2001 Rusty Russell.
51da177e4SLinus Torvalds  */
6a05f096cSAndy Shevchenko #include <linux/ctype.h>
71da177e4SLinus Torvalds #include <linux/device.h>
81da177e4SLinus Torvalds #include <linux/err.h>
9a05f096cSAndy Shevchenko #include <linux/errno.h>
10a05f096cSAndy Shevchenko #include <linux/kernel.h>
11a05f096cSAndy Shevchenko #include <linux/kstrtox.h>
12a05f096cSAndy Shevchenko #include <linux/module.h>
13a05f096cSAndy Shevchenko #include <linux/moduleparam.h>
140fc79cbcSAndy Shevchenko #include <linux/overflow.h>
1520657f66SDavid Howells #include <linux/security.h>
16a05f096cSAndy Shevchenko #include <linux/slab.h>
17a05f096cSAndy Shevchenko #include <linux/string.h>
181da177e4SLinus Torvalds 
19cf2fde7bSRusty Russell #ifdef CONFIG_SYSFS
20b51d23e4SDan Streetman /* Protects all built-in parameters, modules use their own param_lock */
21907b29ebSRusty Russell static DEFINE_MUTEX(param_lock);
22907b29ebSRusty Russell 
23b51d23e4SDan Streetman /* Use the module's mutex, or if built-in use the built-in mutex */
2420bdc2cfSStephen Rothwell #ifdef CONFIG_MODULES
25b51d23e4SDan Streetman #define KPARAM_MUTEX(mod)	((mod) ? &(mod)->param_lock : &param_lock)
2620bdc2cfSStephen Rothwell #else
2720bdc2cfSStephen Rothwell #define KPARAM_MUTEX(mod)	(&param_lock)
2820bdc2cfSStephen Rothwell #endif
29cf2fde7bSRusty Russell 
check_kparam_locked(struct module * mod)30cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
31cf2fde7bSRusty Russell {
32cf2fde7bSRusty Russell 	BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod)));
33cf2fde7bSRusty Russell }
34cf2fde7bSRusty Russell #else
check_kparam_locked(struct module * mod)35cf2fde7bSRusty Russell static inline void check_kparam_locked(struct module *mod)
36cf2fde7bSRusty Russell {
37cf2fde7bSRusty Russell }
38cf2fde7bSRusty Russell #endif /* !CONFIG_SYSFS */
39b51d23e4SDan Streetman 
40a1054322SRusty Russell /* This just allows us to keep track of which parameters are kmalloced. */
41a1054322SRusty Russell struct kmalloced_param {
42a1054322SRusty Russell 	struct list_head list;
43a1054322SRusty Russell 	char val[];
44a1054322SRusty Russell };
45a1054322SRusty Russell static LIST_HEAD(kmalloced_params);
46b51d23e4SDan Streetman static DEFINE_SPINLOCK(kmalloced_params_lock);
47a1054322SRusty Russell 
kmalloc_parameter(unsigned int size)48a1054322SRusty Russell static void *kmalloc_parameter(unsigned int size)
49a1054322SRusty Russell {
50a1054322SRusty Russell 	struct kmalloced_param *p;
51a1054322SRusty Russell 
520fc79cbcSAndy Shevchenko 	p = kmalloc(size_add(sizeof(*p), size), GFP_KERNEL);
53a1054322SRusty Russell 	if (!p)
54a1054322SRusty Russell 		return NULL;
55a1054322SRusty Russell 
56b51d23e4SDan Streetman 	spin_lock(&kmalloced_params_lock);
57a1054322SRusty Russell 	list_add(&p->list, &kmalloced_params);
58b51d23e4SDan Streetman 	spin_unlock(&kmalloced_params_lock);
59b51d23e4SDan Streetman 
60a1054322SRusty Russell 	return p->val;
61a1054322SRusty Russell }
62a1054322SRusty Russell 
63a1054322SRusty Russell /* Does nothing if parameter wasn't kmalloced above. */
maybe_kfree_parameter(void * param)64a1054322SRusty Russell static void maybe_kfree_parameter(void *param)
65a1054322SRusty Russell {
66a1054322SRusty Russell 	struct kmalloced_param *p;
67a1054322SRusty Russell 
68b51d23e4SDan Streetman 	spin_lock(&kmalloced_params_lock);
69a1054322SRusty Russell 	list_for_each_entry(p, &kmalloced_params, list) {
70a1054322SRusty Russell 		if (p->val == param) {
71a1054322SRusty Russell 			list_del(&p->list);
72a1054322SRusty Russell 			kfree(p);
73a1054322SRusty Russell 			break;
74a1054322SRusty Russell 		}
75a1054322SRusty Russell 	}
76b51d23e4SDan Streetman 	spin_unlock(&kmalloced_params_lock);
77a1054322SRusty Russell }
78a1054322SRusty Russell 
dash2underscore(char c)79b1e4d20cSMichal Schmidt static char dash2underscore(char c)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	if (c == '-')
821da177e4SLinus Torvalds 		return '_';
831da177e4SLinus Torvalds 	return c;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
parameqn(const char * a,const char * b,size_t n)86b1e4d20cSMichal Schmidt bool parameqn(const char *a, const char *b, size_t n)
871da177e4SLinus Torvalds {
88b1e4d20cSMichal Schmidt 	size_t i;
89b1e4d20cSMichal Schmidt 
90b1e4d20cSMichal Schmidt 	for (i = 0; i < n; i++) {
91b1e4d20cSMichal Schmidt 		if (dash2underscore(a[i]) != dash2underscore(b[i]))
92b1e4d20cSMichal Schmidt 			return false;
93b1e4d20cSMichal Schmidt 	}
94b1e4d20cSMichal Schmidt 	return true;
95b1e4d20cSMichal Schmidt }
96b1e4d20cSMichal Schmidt 
parameq(const char * a,const char * b)97b1e4d20cSMichal Schmidt bool parameq(const char *a, const char *b)
98b1e4d20cSMichal Schmidt {
99b1e4d20cSMichal Schmidt 	return parameqn(a, b, strlen(a)+1);
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
param_check_unsafe(const struct kernel_param * kp)10220657f66SDavid Howells static bool param_check_unsafe(const struct kernel_param *kp)
1037a486d37SRusty Russell {
10420657f66SDavid Howells 	if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&
10520657f66SDavid Howells 	    security_locked_down(LOCKDOWN_MODULE_PARAMETERS))
10620657f66SDavid Howells 		return false;
10720657f66SDavid Howells 
1087a486d37SRusty Russell 	if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
109edc41b3cSChris Wilson 		pr_notice("Setting dangerous option %s - tainting kernel\n",
1107a486d37SRusty Russell 			  kp->name);
1117a486d37SRusty Russell 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
1127a486d37SRusty Russell 	}
11320657f66SDavid Howells 
11420657f66SDavid Howells 	return true;
1157a486d37SRusty Russell }
1167a486d37SRusty 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,parse_unknown_fn handle_unknown)1171da177e4SLinus Torvalds static int parse_one(char *param,
1181da177e4SLinus Torvalds 		     char *val,
1199fb48c74SJim Cromie 		     const char *doing,
120914dcaa8SRusty Russell 		     const struct kernel_param *params,
1211da177e4SLinus Torvalds 		     unsigned num_params,
122026cee00SPawel Moll 		     s16 min_level,
123026cee00SPawel Moll 		     s16 max_level,
12412cd3cd8SAndy Shevchenko 		     void *arg, parse_unknown_fn handle_unknown)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	unsigned int i;
127907b29ebSRusty Russell 	int err;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	/* Find parameter */
1301da177e4SLinus Torvalds 	for (i = 0; i < num_params; i++) {
1311da177e4SLinus Torvalds 		if (parameq(param, params[i].name)) {
132026cee00SPawel Moll 			if (params[i].level < min_level
133026cee00SPawel Moll 			    || params[i].level > max_level)
134026cee00SPawel Moll 				return 0;
1352e9fb995SRusty Russell 			/* No one handled NULL, so do it here. */
136ab013c5fSSteven Rostedt 			if (!val &&
1376a4c2643SJani Nikula 			    !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
1382e9fb995SRusty Russell 				return -EINVAL;
1399fb48c74SJim Cromie 			pr_debug("handling %s with %p\n", param,
1409bbb9e5aSRusty Russell 				params[i].ops->set);
141b51d23e4SDan Streetman 			kernel_param_lock(params[i].mod);
14220657f66SDavid Howells 			if (param_check_unsafe(&params[i]))
143907b29ebSRusty Russell 				err = params[i].ops->set(val, &params[i]);
14420657f66SDavid Howells 			else
14520657f66SDavid Howells 				err = -EPERM;
146b51d23e4SDan Streetman 			kernel_param_unlock(params[i].mod);
147907b29ebSRusty Russell 			return err;
1481da177e4SLinus Torvalds 		}
1491da177e4SLinus Torvalds 	}
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	if (handle_unknown) {
1529fb48c74SJim Cromie 		pr_debug("doing %s: %s='%s'\n", doing, param, val);
153ecc86170SLuis R. Rodriguez 		return handle_unknown(param, val, doing, arg);
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds 
1569fb48c74SJim Cromie 	pr_debug("Unknown argument '%s'\n", param);
1571da177e4SLinus Torvalds 	return -ENOENT;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
1601da177e4SLinus 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,parse_unknown_fn unknown)16151e158c1SRusty Russell char *parse_args(const char *doing,
1621da177e4SLinus Torvalds 		 char *args,
163914dcaa8SRusty Russell 		 const struct kernel_param *params,
1641da177e4SLinus Torvalds 		 unsigned num,
165026cee00SPawel Moll 		 s16 min_level,
166026cee00SPawel Moll 		 s16 max_level,
16712cd3cd8SAndy Shevchenko 		 void *arg, parse_unknown_fn unknown)
1681da177e4SLinus Torvalds {
16974b22c46SOleg Nesterov 	char *param, *val, *err = NULL;
1701da177e4SLinus Torvalds 
171f36462f0SRusty Russell 	/* Chew leading spaces */
172e7d2860bSAndré Goddard Rosa 	args = skip_spaces(args);
173f36462f0SRusty Russell 
1741ef9eaf2SJim Cromie 	if (*args)
1759fb48c74SJim Cromie 		pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
1769fb48c74SJim Cromie 
1771da177e4SLinus Torvalds 	while (*args) {
1781da177e4SLinus Torvalds 		int ret;
179a416aba6SArd van Breemen 		int irq_was_disabled;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 		args = next_arg(args, &param, &val);
18251e158c1SRusty Russell 		/* Stop at -- */
18351e158c1SRusty Russell 		if (!val && strcmp(param, "--") == 0)
18474b22c46SOleg Nesterov 			return err ?: args;
185a416aba6SArd van Breemen 		irq_was_disabled = irqs_disabled();
1869fb48c74SJim Cromie 		ret = parse_one(param, val, doing, params, num,
187ecc86170SLuis R. Rodriguez 				min_level, max_level, arg, unknown);
188b5f3abf9SJim Cromie 		if (irq_was_disabled && !irqs_disabled())
189b5f3abf9SJim Cromie 			pr_warn("%s: option '%s' enabled irq's!\n",
190b5f3abf9SJim Cromie 				doing, param);
191b5f3abf9SJim Cromie 
1921da177e4SLinus Torvalds 		switch (ret) {
19374b22c46SOleg Nesterov 		case 0:
19474b22c46SOleg Nesterov 			continue;
1951da177e4SLinus Torvalds 		case -ENOENT:
196b5f3abf9SJim Cromie 			pr_err("%s: Unknown parameter `%s'\n", doing, param);
19774b22c46SOleg Nesterov 			break;
1981da177e4SLinus Torvalds 		case -ENOSPC:
199b5f3abf9SJim Cromie 			pr_err("%s: `%s' too large for parameter `%s'\n",
2009fb48c74SJim Cromie 			       doing, val ?: "", param);
2011da177e4SLinus Torvalds 			break;
2021da177e4SLinus Torvalds 		default:
203b5f3abf9SJim Cromie 			pr_err("%s: `%s' invalid for parameter `%s'\n",
2049fb48c74SJim Cromie 			       doing, val ?: "", param);
20574b22c46SOleg Nesterov 			break;
2061da177e4SLinus Torvalds 		}
2071da177e4SLinus Torvalds 
20874b22c46SOleg Nesterov 		err = ERR_PTR(ret);
20974b22c46SOleg Nesterov 	}
21074b22c46SOleg Nesterov 
21174b22c46SOleg Nesterov 	return err;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds /* Lazy bastard, eh? */
21588a88b32SFelipe Contreras #define STANDARD_PARAM_DEF(name, type, format, strtolfn)      		\
2169bbb9e5aSRusty Russell 	int param_set_##name(const char *val, const struct kernel_param *kp) \
2171da177e4SLinus Torvalds 	{								\
21888a88b32SFelipe Contreras 		return strtolfn(val, 0, (type *)kp->arg);		\
2191da177e4SLinus Torvalds 	}								\
2209bbb9e5aSRusty Russell 	int param_get_##name(char *buffer, const struct kernel_param *kp) \
2211da177e4SLinus Torvalds 	{								\
22296802e6bSJean Delvare 		return scnprintf(buffer, PAGE_SIZE, format "\n",	\
223f4940ab7SChen Gang 				*((type *)kp->arg));			\
224a14fe249SRusty Russell 	}								\
2259c27847dSLuis R. Rodriguez 	const struct kernel_param_ops param_ops_##name = {			\
2269bbb9e5aSRusty Russell 		.set = param_set_##name,				\
2279bbb9e5aSRusty Russell 		.get = param_get_##name,				\
2289bbb9e5aSRusty Russell 	};								\
229a14fe249SRusty Russell 	EXPORT_SYMBOL(param_set_##name);				\
2309bbb9e5aSRusty Russell 	EXPORT_SYMBOL(param_get_##name);				\
2319bbb9e5aSRusty Russell 	EXPORT_SYMBOL(param_ops_##name)
2329bbb9e5aSRusty Russell 
2331da177e4SLinus Torvalds 
23488a88b32SFelipe Contreras STANDARD_PARAM_DEF(byte,	unsigned char,		"%hhu",		kstrtou8);
23588a88b32SFelipe Contreras STANDARD_PARAM_DEF(short,	short,			"%hi",		kstrtos16);
23688a88b32SFelipe Contreras STANDARD_PARAM_DEF(ushort,	unsigned short,		"%hu",		kstrtou16);
23788a88b32SFelipe Contreras STANDARD_PARAM_DEF(int,		int,			"%i",		kstrtoint);
23888a88b32SFelipe Contreras STANDARD_PARAM_DEF(uint,	unsigned int,		"%u",		kstrtouint);
23988a88b32SFelipe Contreras STANDARD_PARAM_DEF(long,	long,			"%li",		kstrtol);
24088a88b32SFelipe Contreras STANDARD_PARAM_DEF(ulong,	unsigned long,		"%lu",		kstrtoul);
241b4210b81SHannes Reinecke STANDARD_PARAM_DEF(ullong,	unsigned long long,	"%llu",		kstrtoull);
2427d836577SPaul Menzel STANDARD_PARAM_DEF(hexint,	unsigned int,		"%#08x", 	kstrtouint);
2431da177e4SLinus Torvalds 
param_set_uint_minmax(const char * val,const struct kernel_param * kp,unsigned int min,unsigned int max)2442a14c9aeSSagi Grimberg int param_set_uint_minmax(const char *val, const struct kernel_param *kp,
2452a14c9aeSSagi Grimberg 		unsigned int min, unsigned int max)
2462a14c9aeSSagi Grimberg {
2472a14c9aeSSagi Grimberg 	unsigned int num;
2482a14c9aeSSagi Grimberg 	int ret;
2492a14c9aeSSagi Grimberg 
2502a14c9aeSSagi Grimberg 	if (!val)
2512a14c9aeSSagi Grimberg 		return -EINVAL;
2522a14c9aeSSagi Grimberg 	ret = kstrtouint(val, 0, &num);
2532a14c9aeSSagi Grimberg 	if (ret)
2542a14c9aeSSagi Grimberg 		return ret;
2552a14c9aeSSagi Grimberg 	if (num < min || num > max)
2562a14c9aeSSagi Grimberg 		return -EINVAL;
2572a14c9aeSSagi Grimberg 	*((unsigned int *)kp->arg) = num;
2582a14c9aeSSagi Grimberg 	return 0;
2592a14c9aeSSagi Grimberg }
2602a14c9aeSSagi Grimberg EXPORT_SYMBOL_GPL(param_set_uint_minmax);
2612a14c9aeSSagi Grimberg 
param_set_charp(const char * val,const struct kernel_param * kp)2629bbb9e5aSRusty Russell int param_set_charp(const char *val, const struct kernel_param *kp)
2631da177e4SLinus Torvalds {
264fd0cd057SAndy Shevchenko 	size_t len, maxlen = 1024;
265fd0cd057SAndy Shevchenko 
266fd0cd057SAndy Shevchenko 	len = strnlen(val, maxlen + 1);
267fd0cd057SAndy Shevchenko 	if (len == maxlen + 1) {
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 
274b5e3f86aSAndy Shevchenko 	/*
275b5e3f86aSAndy Shevchenko 	 * This is a hack. We can't kmalloc() in early boot, and we
276b5e3f86aSAndy Shevchenko 	 * don't need to; this mangled commandline is preserved.
277b5e3f86aSAndy Shevchenko 	 */
278e180a6b7SRusty Russell 	if (slab_is_available()) {
279fd0cd057SAndy Shevchenko 		*(char **)kp->arg = kmalloc_parameter(len + 1);
280d553ad86SRusty Russell 		if (!*(char **)kp->arg)
281e180a6b7SRusty Russell 			return -ENOMEM;
282a1054322SRusty Russell 		strcpy(*(char **)kp->arg, val);
283e180a6b7SRusty Russell 	} else
284e180a6b7SRusty Russell 		*(const char **)kp->arg = val;
285e180a6b7SRusty Russell 
2861da177e4SLinus Torvalds 	return 0;
2871da177e4SLinus Torvalds }
288a14fe249SRusty Russell EXPORT_SYMBOL(param_set_charp);
2891da177e4SLinus Torvalds 
param_get_charp(char * buffer,const struct kernel_param * kp)2909bbb9e5aSRusty Russell int param_get_charp(char *buffer, const struct kernel_param *kp)
2911da177e4SLinus Torvalds {
29296802e6bSJean Delvare 	return scnprintf(buffer, PAGE_SIZE, "%s\n", *((char **)kp->arg));
2931da177e4SLinus Torvalds }
294a14fe249SRusty Russell EXPORT_SYMBOL(param_get_charp);
2951da177e4SLinus Torvalds 
param_free_charp(void * arg)2963d9c637fSDan Streetman void param_free_charp(void *arg)
297a1054322SRusty Russell {
298a1054322SRusty Russell 	maybe_kfree_parameter(*((char **)arg));
299a1054322SRusty Russell }
3003d9c637fSDan Streetman EXPORT_SYMBOL(param_free_charp);
301a1054322SRusty Russell 
3029c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_charp = {
3039bbb9e5aSRusty Russell 	.set = param_set_charp,
3049bbb9e5aSRusty Russell 	.get = param_get_charp,
305a1054322SRusty Russell 	.free = param_free_charp,
3069bbb9e5aSRusty Russell };
3079bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_charp);
3089bbb9e5aSRusty Russell 
309fddd5201SRusty Russell /* Actually could be a bool or an int, for historical reasons. */
param_set_bool(const char * val,const struct kernel_param * kp)3109bbb9e5aSRusty Russell int param_set_bool(const char *val, const struct kernel_param *kp)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	/* No equals means "set"... */
3131da177e4SLinus Torvalds 	if (!val) val = "1";
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	/* One of =[yYnN01] */
316def7b92eSChristophe JAILLET 	return kstrtobool(val, kp->arg);
317fddd5201SRusty Russell }
318a14fe249SRusty Russell EXPORT_SYMBOL(param_set_bool);
319fddd5201SRusty Russell 
param_get_bool(char * buffer,const struct kernel_param * kp)3209bbb9e5aSRusty Russell int param_get_bool(char *buffer, const struct kernel_param *kp)
3211da177e4SLinus Torvalds {
3221da177e4SLinus Torvalds 	/* Y and N chosen as being relatively non-coder friendly */
32396802e6bSJean Delvare 	return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N');
3241da177e4SLinus Torvalds }
325a14fe249SRusty Russell EXPORT_SYMBOL(param_get_bool);
3261da177e4SLinus Torvalds 
3279c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bool = {
3286a4c2643SJani Nikula 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
3299bbb9e5aSRusty Russell 	.set = param_set_bool,
3309bbb9e5aSRusty Russell 	.get = param_get_bool,
3319bbb9e5aSRusty Russell };
3329bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_bool);
3339bbb9e5aSRusty Russell 
param_set_bool_enable_only(const char * val,const struct kernel_param * kp)334d19f05d8SLuis R. Rodriguez int param_set_bool_enable_only(const char *val, const struct kernel_param *kp)
335d19f05d8SLuis R. Rodriguez {
3369ce170ceSLi zeming 	int err;
337d19f05d8SLuis R. Rodriguez 	bool new_value;
338d19f05d8SLuis R. Rodriguez 	bool orig_value = *(bool *)kp->arg;
339d19f05d8SLuis R. Rodriguez 	struct kernel_param dummy_kp = *kp;
340d19f05d8SLuis R. Rodriguez 
341d19f05d8SLuis R. Rodriguez 	dummy_kp.arg = &new_value;
342d19f05d8SLuis R. Rodriguez 
343d19f05d8SLuis R. Rodriguez 	err = param_set_bool(val, &dummy_kp);
344d19f05d8SLuis R. Rodriguez 	if (err)
345d19f05d8SLuis R. Rodriguez 		return err;
346d19f05d8SLuis R. Rodriguez 
347d19f05d8SLuis R. Rodriguez 	/* Don't let them unset it once it's set! */
348d19f05d8SLuis R. Rodriguez 	if (!new_value && orig_value)
349d19f05d8SLuis R. Rodriguez 		return -EROFS;
350d19f05d8SLuis R. Rodriguez 
351d19f05d8SLuis R. Rodriguez 	if (new_value)
352d19f05d8SLuis R. Rodriguez 		err = param_set_bool(val, kp);
353d19f05d8SLuis R. Rodriguez 
354d19f05d8SLuis R. Rodriguez 	return err;
355d19f05d8SLuis R. Rodriguez }
356d19f05d8SLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_set_bool_enable_only);
357d19f05d8SLuis R. Rodriguez 
358d19f05d8SLuis R. Rodriguez const struct kernel_param_ops param_ops_bool_enable_only = {
359d19f05d8SLuis R. Rodriguez 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
360d19f05d8SLuis R. Rodriguez 	.set = param_set_bool_enable_only,
361d19f05d8SLuis R. Rodriguez 	.get = param_get_bool,
362d19f05d8SLuis R. Rodriguez };
363154be21cSLuis R. Rodriguez EXPORT_SYMBOL_GPL(param_ops_bool_enable_only);
364d19f05d8SLuis R. Rodriguez 
365fddd5201SRusty Russell /* This one must be bool. */
param_set_invbool(const char * val,const struct kernel_param * kp)3669bbb9e5aSRusty Russell int param_set_invbool(const char *val, const struct kernel_param *kp)
3671da177e4SLinus Torvalds {
368fddd5201SRusty Russell 	int ret;
369fddd5201SRusty Russell 	bool boolval;
37022e48eafSJan Beulich 	struct kernel_param dummy;
3711da177e4SLinus Torvalds 
37222e48eafSJan Beulich 	dummy.arg = &boolval;
3731da177e4SLinus Torvalds 	ret = param_set_bool(val, &dummy);
3741da177e4SLinus Torvalds 	if (ret == 0)
3759a71af2cSRusty Russell 		*(bool *)kp->arg = !boolval;
3761da177e4SLinus Torvalds 	return ret;
3771da177e4SLinus Torvalds }
378a14fe249SRusty Russell EXPORT_SYMBOL(param_set_invbool);
3791da177e4SLinus Torvalds 
param_get_invbool(char * buffer,const struct kernel_param * kp)3809bbb9e5aSRusty Russell int param_get_invbool(char *buffer, const struct kernel_param *kp)
3811da177e4SLinus Torvalds {
38296802e6bSJean Delvare 	return sprintf(buffer, "%c\n", (*(bool *)kp->arg) ? 'N' : 'Y');
3831da177e4SLinus Torvalds }
384a14fe249SRusty Russell EXPORT_SYMBOL(param_get_invbool);
3851da177e4SLinus Torvalds 
3869c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_invbool = {
3879bbb9e5aSRusty Russell 	.set = param_set_invbool,
3889bbb9e5aSRusty Russell 	.get = param_get_invbool,
3899bbb9e5aSRusty Russell };
3909bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_invbool);
3919bbb9e5aSRusty Russell 
param_set_bint(const char * val,const struct kernel_param * kp)39269116f27SRusty Russell int param_set_bint(const char *val, const struct kernel_param *kp)
39369116f27SRusty Russell {
3945104b7d7SDan Streetman 	/* Match bool exactly, by re-using it. */
3955104b7d7SDan Streetman 	struct kernel_param boolkp = *kp;
39669116f27SRusty Russell 	bool v;
39769116f27SRusty Russell 	int ret;
39869116f27SRusty Russell 
39969116f27SRusty Russell 	boolkp.arg = &v;
40069116f27SRusty Russell 
40169116f27SRusty Russell 	ret = param_set_bool(val, &boolkp);
40269116f27SRusty Russell 	if (ret == 0)
40369116f27SRusty Russell 		*(int *)kp->arg = v;
40469116f27SRusty Russell 	return ret;
40569116f27SRusty Russell }
40669116f27SRusty Russell EXPORT_SYMBOL(param_set_bint);
40769116f27SRusty Russell 
4089c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_bint = {
4096a4c2643SJani Nikula 	.flags = KERNEL_PARAM_OPS_FL_NOARG,
41069116f27SRusty Russell 	.set = param_set_bint,
41169116f27SRusty Russell 	.get = param_get_int,
41269116f27SRusty Russell };
41369116f27SRusty Russell EXPORT_SYMBOL(param_ops_bint);
41469116f27SRusty Russell 
4159730b5b0SBert 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)416b51d23e4SDan Streetman static int param_array(struct module *mod,
417b51d23e4SDan Streetman 		       const char *name,
4181da177e4SLinus Torvalds 		       const char *val,
4191da177e4SLinus Torvalds 		       unsigned int min, unsigned int max,
4201da177e4SLinus Torvalds 		       void *elem, int elemsize,
4219bbb9e5aSRusty Russell 		       int (*set)(const char *, const struct kernel_param *kp),
422026cee00SPawel Moll 		       s16 level,
423eb38a996SRichard Knutsson 		       unsigned int *num)
4241da177e4SLinus Torvalds {
4251da177e4SLinus Torvalds 	int ret;
4261da177e4SLinus Torvalds 	struct kernel_param kp;
4271da177e4SLinus Torvalds 	char save;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 	/* Get the name right for errors. */
4301da177e4SLinus Torvalds 	kp.name = name;
4311da177e4SLinus Torvalds 	kp.arg = elem;
432026cee00SPawel Moll 	kp.level = level;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	*num = 0;
4351da177e4SLinus Torvalds 	/* We expect a comma-separated list of values. */
4361da177e4SLinus Torvalds 	do {
4371da177e4SLinus Torvalds 		int len;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 		if (*num == max) {
440b5f3abf9SJim Cromie 			pr_err("%s: can only take %i arguments\n", name, max);
4411da177e4SLinus Torvalds 			return -EINVAL;
4421da177e4SLinus Torvalds 		}
4431da177e4SLinus Torvalds 		len = strcspn(val, ",");
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 		/* nul-terminate and parse */
4461da177e4SLinus Torvalds 		save = val[len];
4471da177e4SLinus Torvalds 		((char *)val)[len] = '\0';
448cf2fde7bSRusty Russell 		check_kparam_locked(mod);
4491da177e4SLinus Torvalds 		ret = set(val, &kp);
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 		if (ret != 0)
4521da177e4SLinus Torvalds 			return ret;
4531da177e4SLinus Torvalds 		kp.arg += elemsize;
4541da177e4SLinus Torvalds 		val += len+1;
4551da177e4SLinus Torvalds 		(*num)++;
4561da177e4SLinus Torvalds 	} while (save == ',');
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	if (*num < min) {
459b5f3abf9SJim Cromie 		pr_err("%s: needs at least %i arguments\n", name, min);
4601da177e4SLinus Torvalds 		return -EINVAL;
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 	return 0;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
param_array_set(const char * val,const struct kernel_param * kp)4659bbb9e5aSRusty Russell static int param_array_set(const char *val, const struct kernel_param *kp)
4661da177e4SLinus Torvalds {
46722e48eafSJan Beulich 	const struct kparam_array *arr = kp->arr;
46831143a12SBert Wesarg 	unsigned int temp_num;
4691da177e4SLinus Torvalds 
470b51d23e4SDan Streetman 	return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
471026cee00SPawel Moll 			   arr->elemsize, arr->ops->set, kp->level,
4723c7d76e3SRusty Russell 			   arr->num ?: &temp_num);
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds 
param_array_get(char * buffer,const struct kernel_param * kp)4759bbb9e5aSRusty Russell static int param_array_get(char *buffer, const struct kernel_param *kp)
4761da177e4SLinus Torvalds {
4771da177e4SLinus Torvalds 	int i, off, ret;
47822e48eafSJan Beulich 	const struct kparam_array *arr = kp->arr;
4795104b7d7SDan Streetman 	struct kernel_param p = *kp;
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
48296802e6bSJean Delvare 		/* Replace \n with comma */
4831da177e4SLinus Torvalds 		if (i)
48496802e6bSJean Delvare 			buffer[off - 1] = ',';
4851da177e4SLinus Torvalds 		p.arg = arr->elem + arr->elemsize * i;
486cf2fde7bSRusty Russell 		check_kparam_locked(p.mod);
4879bbb9e5aSRusty Russell 		ret = arr->ops->get(buffer + off, &p);
4881da177e4SLinus Torvalds 		if (ret < 0)
4891da177e4SLinus Torvalds 			return ret;
4901da177e4SLinus Torvalds 		off += ret;
4911da177e4SLinus Torvalds 	}
4921da177e4SLinus Torvalds 	buffer[off] = '\0';
4931da177e4SLinus Torvalds 	return off;
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds 
param_array_free(void * arg)496e6df34a4SRusty Russell static void param_array_free(void *arg)
497e6df34a4SRusty Russell {
498e6df34a4SRusty Russell 	unsigned int i;
499e6df34a4SRusty Russell 	const struct kparam_array *arr = arg;
500e6df34a4SRusty Russell 
501e6df34a4SRusty Russell 	if (arr->ops->free)
502e6df34a4SRusty Russell 		for (i = 0; i < (arr->num ? *arr->num : arr->max); i++)
503e6df34a4SRusty Russell 			arr->ops->free(arr->elem + arr->elemsize * i);
504e6df34a4SRusty Russell }
505e6df34a4SRusty Russell 
5069c27847dSLuis R. Rodriguez const struct kernel_param_ops param_array_ops = {
5079bbb9e5aSRusty Russell 	.set = param_array_set,
5089bbb9e5aSRusty Russell 	.get = param_array_get,
509e6df34a4SRusty Russell 	.free = param_array_free,
5109bbb9e5aSRusty Russell };
5119bbb9e5aSRusty Russell EXPORT_SYMBOL(param_array_ops);
5129bbb9e5aSRusty Russell 
param_set_copystring(const char * val,const struct kernel_param * kp)5139bbb9e5aSRusty Russell int param_set_copystring(const char *val, const struct kernel_param *kp)
5141da177e4SLinus Torvalds {
51522e48eafSJan Beulich 	const struct kparam_string *kps = kp->str;
5161da177e4SLinus Torvalds 
517fd0cd057SAndy Shevchenko 	if (strnlen(val, kps->maxlen) == kps->maxlen) {
518b5f3abf9SJim Cromie 		pr_err("%s: string doesn't fit in %u chars.\n",
5191da177e4SLinus Torvalds 		       kp->name, kps->maxlen-1);
5201da177e4SLinus Torvalds 		return -ENOSPC;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 	strcpy(kps->string, val);
5231da177e4SLinus Torvalds 	return 0;
5241da177e4SLinus Torvalds }
525a14fe249SRusty Russell EXPORT_SYMBOL(param_set_copystring);
5261da177e4SLinus Torvalds 
param_get_string(char * buffer,const struct kernel_param * kp)5279bbb9e5aSRusty Russell int param_get_string(char *buffer, const struct kernel_param *kp)
5281da177e4SLinus Torvalds {
52922e48eafSJan Beulich 	const struct kparam_string *kps = kp->str;
53096802e6bSJean Delvare 	return scnprintf(buffer, PAGE_SIZE, "%s\n", kps->string);
5311da177e4SLinus Torvalds }
532a14fe249SRusty Russell EXPORT_SYMBOL(param_get_string);
5331da177e4SLinus Torvalds 
5349c27847dSLuis R. Rodriguez const struct kernel_param_ops param_ops_string = {
5359bbb9e5aSRusty Russell 	.set = param_set_copystring,
5369bbb9e5aSRusty Russell 	.get = param_get_string,
5379bbb9e5aSRusty Russell };
5389bbb9e5aSRusty Russell EXPORT_SYMBOL(param_ops_string);
5399bbb9e5aSRusty Russell 
5401da177e4SLinus Torvalds /* sysfs output in /sys/modules/XYZ/parameters/ */
541f3227ffdSThomas Weißschuh #define to_module_attr(n) container_of_const(n, struct module_attribute, attr)
542350f8258SEdward Z. Yang #define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds struct param_attribute
5451da177e4SLinus Torvalds {
5461da177e4SLinus Torvalds 	struct module_attribute mattr;
5479bbb9e5aSRusty Russell 	const struct kernel_param *param;
5481da177e4SLinus Torvalds };
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds struct module_param_attrs
5511da177e4SLinus Torvalds {
5529b473de8SRusty Russell 	unsigned int num;
5531da177e4SLinus Torvalds 	struct attribute_group grp;
5543690f4a8SThorsten Blum 	struct param_attribute attrs[] __counted_by(num);
5551da177e4SLinus Torvalds };
5561da177e4SLinus Torvalds 
557ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
55830d44608SThomas Weißschuh #define to_param_attr(n) container_of_const(n, struct param_attribute, mattr)
5591da177e4SLinus Torvalds 
param_attr_show(const struct module_attribute * mattr,struct module_kobject * mk,char * buf)560f3227ffdSThomas Weißschuh static ssize_t param_attr_show(const struct module_attribute *mattr,
5614befb026SKay Sievers 			       struct module_kobject *mk, char *buf)
5621da177e4SLinus Torvalds {
5631da177e4SLinus Torvalds 	int count;
56430d44608SThomas Weißschuh 	const struct param_attribute *attribute = to_param_attr(mattr);
5651da177e4SLinus Torvalds 
5669bbb9e5aSRusty Russell 	if (!attribute->param->ops->get)
5671da177e4SLinus Torvalds 		return -EPERM;
5681da177e4SLinus Torvalds 
569b51d23e4SDan Streetman 	kernel_param_lock(mk->mod);
5709bbb9e5aSRusty Russell 	count = attribute->param->ops->get(buf, attribute->param);
571b51d23e4SDan Streetman 	kernel_param_unlock(mk->mod);
5721da177e4SLinus Torvalds 	return count;
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds /* sysfs always hands a nul-terminated string in buf.  We rely on that. */
param_attr_store(const struct module_attribute * mattr,struct module_kobject * mk,const char * buf,size_t len)576f3227ffdSThomas Weißschuh static ssize_t param_attr_store(const struct module_attribute *mattr,
577b51d23e4SDan Streetman 				struct module_kobject *mk,
5781da177e4SLinus Torvalds 				const char *buf, size_t len)
5791da177e4SLinus Torvalds {
5801da177e4SLinus Torvalds  	int err;
58130d44608SThomas Weißschuh 	const struct param_attribute *attribute = to_param_attr(mattr);
5821da177e4SLinus Torvalds 
5839bbb9e5aSRusty Russell 	if (!attribute->param->ops->set)
5841da177e4SLinus Torvalds 		return -EPERM;
5851da177e4SLinus Torvalds 
586b51d23e4SDan Streetman 	kernel_param_lock(mk->mod);
58720657f66SDavid Howells 	if (param_check_unsafe(attribute->param))
5889bbb9e5aSRusty Russell 		err = attribute->param->ops->set(buf, attribute->param);
58920657f66SDavid Howells 	else
59020657f66SDavid Howells 		err = -EPERM;
591b51d23e4SDan Streetman 	kernel_param_unlock(mk->mod);
5921da177e4SLinus Torvalds 	if (!err)
5931da177e4SLinus Torvalds 		return len;
5941da177e4SLinus Torvalds 	return err;
5951da177e4SLinus Torvalds }
596ef665c1aSRandy Dunlap #endif
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds #ifdef CONFIG_MODULES
5991da177e4SLinus Torvalds #define __modinit
6001da177e4SLinus Torvalds #else
6011da177e4SLinus Torvalds #define __modinit __init
6021da177e4SLinus Torvalds #endif
6031da177e4SLinus Torvalds 
604ef665c1aSRandy Dunlap #ifdef CONFIG_SYSFS
kernel_param_lock(struct module * mod)605b51d23e4SDan Streetman void kernel_param_lock(struct module *mod)
606907b29ebSRusty Russell {
607b51d23e4SDan Streetman 	mutex_lock(KPARAM_MUTEX(mod));
608907b29ebSRusty Russell }
609907b29ebSRusty Russell 
kernel_param_unlock(struct module * mod)610b51d23e4SDan Streetman void kernel_param_unlock(struct module *mod)
611907b29ebSRusty Russell {
612b51d23e4SDan Streetman 	mutex_unlock(KPARAM_MUTEX(mod));
613907b29ebSRusty Russell }
614b51d23e4SDan Streetman 
615b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_lock);
616b51d23e4SDan Streetman EXPORT_SYMBOL(kernel_param_unlock);
617907b29ebSRusty Russell 
6181da177e4SLinus Torvalds /*
6199b473de8SRusty Russell  * add_sysfs_param - add a parameter to sysfs
6209b473de8SRusty Russell  * @mk: struct module_kobject
621630cc2b3SJean Delvare  * @kp: the actual parameter definition to add to sysfs
6229b473de8SRusty Russell  * @name: name of parameter
6231da177e4SLinus Torvalds  *
6249b473de8SRusty Russell  * Create a kobject if for a (per-module) parameter if mp NULL, and
6259b473de8SRusty Russell  * create file in sysfs.  Returns an error on out of memory.  Always cleans up
6269b473de8SRusty Russell  * if there's an error.
6271da177e4SLinus Torvalds  */
add_sysfs_param(struct module_kobject * mk,const struct kernel_param * kp,const char * name)6289b473de8SRusty Russell static __modinit int add_sysfs_param(struct module_kobject *mk,
6299bbb9e5aSRusty Russell 				     const struct kernel_param *kp,
6309b473de8SRusty Russell 				     const char *name)
6311da177e4SLinus Torvalds {
63218eb74faSRusty Russell 	struct module_param_attrs *new_mp;
63318eb74faSRusty Russell 	struct attribute **new_attrs;
63418eb74faSRusty Russell 	unsigned int i;
6351da177e4SLinus Torvalds 
6369b473de8SRusty Russell 	/* We don't bother calling this with invisible parameters. */
6379b473de8SRusty Russell 	BUG_ON(!kp->perm);
6389b473de8SRusty Russell 
6399b473de8SRusty Russell 	if (!mk->mp) {
64018eb74faSRusty Russell 		/* First allocation. */
64118eb74faSRusty Russell 		mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
64218eb74faSRusty Russell 		if (!mk->mp)
64318eb74faSRusty Russell 			return -ENOMEM;
64418eb74faSRusty Russell 		mk->mp->grp.name = "parameters";
64518eb74faSRusty Russell 		/* NULL-terminated attribute array. */
64618eb74faSRusty Russell 		mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
6479b473de8SRusty Russell 					    GFP_KERNEL);
64818eb74faSRusty Russell 		/* Caller will cleanup via free_module_param_attrs */
64918eb74faSRusty Russell 		if (!mk->mp->grp.attrs)
65018eb74faSRusty Russell 			return -ENOMEM;
6511da177e4SLinus Torvalds 	}
6521da177e4SLinus Torvalds 
65318eb74faSRusty Russell 	/* Enlarge allocations. */
6543690f4a8SThorsten Blum 	new_mp = krealloc(mk->mp, struct_size(mk->mp, attrs, mk->mp->num + 1),
65518eb74faSRusty Russell 			  GFP_KERNEL);
65618eb74faSRusty Russell 	if (!new_mp)
65718eb74faSRusty Russell 		return -ENOMEM;
65818eb74faSRusty Russell 	mk->mp = new_mp;
6593690f4a8SThorsten Blum 	mk->mp->num++;
66018eb74faSRusty Russell 
66118eb74faSRusty Russell 	/* Extra pointer for NULL terminator */
6623690f4a8SThorsten Blum 	new_attrs = krealloc_array(mk->mp->grp.attrs, mk->mp->num + 1,
6633690f4a8SThorsten Blum 				   sizeof(mk->mp->grp.attrs[0]), 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. */
6693690f4a8SThorsten Blum 	memset(&mk->mp->attrs[mk->mp->num - 1], 0, sizeof(mk->mp->attrs[0]));
6703690f4a8SThorsten Blum 	sysfs_attr_init(&mk->mp->attrs[mk->mp->num - 1].mattr.attr);
6713690f4a8SThorsten Blum 	mk->mp->attrs[mk->mp->num - 1].param = kp;
6723690f4a8SThorsten Blum 	mk->mp->attrs[mk->mp->num - 1].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)
6753690f4a8SThorsten Blum 		mk->mp->attrs[mk->mp->num - 1].mattr.store = param_attr_store;
676574732c7SRusty Russell 	else
6773690f4a8SThorsten Blum 		mk->mp->attrs[mk->mp->num - 1].mattr.store = NULL;
6783690f4a8SThorsten Blum 	mk->mp->attrs[mk->mp->num - 1].mattr.attr.name = (char *)name;
6793690f4a8SThorsten Blum 	mk->mp->attrs[mk->mp->num - 1].mattr.attr.mode = kp->perm;
6809b473de8SRusty Russell 
6819b473de8SRusty Russell 	/* Fix up all the pointers, since krealloc can move us */
68218eb74faSRusty Russell 	for (i = 0; i < mk->mp->num; i++)
68318eb74faSRusty Russell 		mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
68418eb74faSRusty Russell 	mk->mp->grp.attrs[mk->mp->num] = NULL;
6859b473de8SRusty Russell 	return 0;
6861da177e4SLinus Torvalds }
6879b473de8SRusty Russell 
688d2441183SLinus Torvalds #ifdef CONFIG_MODULES
free_module_param_attrs(struct module_kobject * mk)6899b473de8SRusty Russell static void free_module_param_attrs(struct module_kobject *mk)
6909b473de8SRusty Russell {
69118eb74faSRusty Russell 	if (mk->mp)
6929b473de8SRusty Russell 		kfree(mk->mp->grp.attrs);
6939b473de8SRusty Russell 	kfree(mk->mp);
6949b473de8SRusty Russell 	mk->mp = NULL;
6951da177e4SLinus Torvalds }
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds /*
6981da177e4SLinus Torvalds  * module_param_sysfs_setup - setup sysfs support for one module
6991da177e4SLinus Torvalds  * @mod: module
7001da177e4SLinus Torvalds  * @kparam: module parameters (array)
7011da177e4SLinus Torvalds  * @num_params: number of module parameters
7021da177e4SLinus Torvalds  *
7039b473de8SRusty Russell  * Adds sysfs entries for module parameters under
7049b473de8SRusty Russell  * /sys/module/[mod->name]/parameters/
7051da177e4SLinus Torvalds  */
module_param_sysfs_setup(struct module * mod,const struct kernel_param * kparam,unsigned int num_params)7061da177e4SLinus Torvalds int module_param_sysfs_setup(struct module *mod,
7079bbb9e5aSRusty Russell 			     const struct kernel_param *kparam,
7081da177e4SLinus Torvalds 			     unsigned int num_params)
7091da177e4SLinus Torvalds {
7109b473de8SRusty Russell 	int i, err;
7119b473de8SRusty Russell 	bool params = false;
7121da177e4SLinus Torvalds 
7139b473de8SRusty Russell 	for (i = 0; i < num_params; i++) {
7149b473de8SRusty Russell 		if (kparam[i].perm == 0)
7159b473de8SRusty Russell 			continue;
7169b473de8SRusty Russell 		err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
71718eb74faSRusty Russell 		if (err) {
71818eb74faSRusty Russell 			free_module_param_attrs(&mod->mkobj);
7199b473de8SRusty Russell 			return err;
72018eb74faSRusty Russell 		}
7219b473de8SRusty Russell 		params = true;
7229b473de8SRusty Russell 	}
7231da177e4SLinus Torvalds 
7249b473de8SRusty Russell 	if (!params)
7251da177e4SLinus Torvalds 		return 0;
7269b473de8SRusty Russell 
7279b473de8SRusty Russell 	/* Create the param group. */
7289b473de8SRusty Russell 	err = sysfs_create_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
7299b473de8SRusty Russell 	if (err)
7309b473de8SRusty Russell 		free_module_param_attrs(&mod->mkobj);
7319b473de8SRusty Russell 	return err;
7321da177e4SLinus Torvalds }
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds /*
7351da177e4SLinus Torvalds  * module_param_sysfs_remove - remove sysfs support for one module
7361da177e4SLinus Torvalds  * @mod: module
7371da177e4SLinus Torvalds  *
7381da177e4SLinus Torvalds  * Remove sysfs entries for module parameters and the corresponding
7391da177e4SLinus Torvalds  * kobject.
7401da177e4SLinus Torvalds  */
module_param_sysfs_remove(struct module * mod)7411da177e4SLinus Torvalds void module_param_sysfs_remove(struct module *mod)
7421da177e4SLinus Torvalds {
7439b473de8SRusty Russell 	if (mod->mkobj.mp) {
7449b473de8SRusty Russell 		sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
745b5e3f86aSAndy Shevchenko 		/*
746b5e3f86aSAndy Shevchenko 		 * We are positive that no one is using any param
747b5e3f86aSAndy Shevchenko 		 * attrs at this point. Deallocate immediately.
748b5e3f86aSAndy Shevchenko 		 */
7499b473de8SRusty Russell 		free_module_param_attrs(&mod->mkobj);
7501da177e4SLinus Torvalds 	}
7511da177e4SLinus Torvalds }
7521da177e4SLinus Torvalds #endif
7531da177e4SLinus Torvalds 
destroy_params(const struct kernel_param * params,unsigned num)754e180a6b7SRusty Russell void destroy_params(const struct kernel_param *params, unsigned num)
755e180a6b7SRusty Russell {
756e6df34a4SRusty Russell 	unsigned int i;
757e6df34a4SRusty Russell 
758e6df34a4SRusty Russell 	for (i = 0; i < num; i++)
759e6df34a4SRusty Russell 		if (params[i].ops->free)
760e6df34a4SRusty Russell 			params[i].ops->free(params[i].arg);
761e180a6b7SRusty Russell }
762e180a6b7SRusty Russell 
lookup_or_create_module_kobject(const char * name)7637c76c813SShyam Saini struct module_kobject __modinit * lookup_or_create_module_kobject(const char *name)
7641da177e4SLinus Torvalds {
7651da177e4SLinus Torvalds 	struct module_kobject *mk;
7669b473de8SRusty Russell 	struct kobject *kobj;
7679b473de8SRusty Russell 	int err;
7681da177e4SLinus Torvalds 
7699b473de8SRusty Russell 	kobj = kset_find_obj(module_kset, name);
7701c7777feSShyam Saini 	if (kobj)
7711c7777feSShyam Saini 		return to_module_kobject(kobj);
7721c7777feSShyam Saini 
773dd392710SPekka J Enberg 	mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
7741c7777feSShyam Saini 	if (!mk)
7751c7777feSShyam Saini 		return NULL;
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	mk->mod = THIS_MODULE;
7787405c1e1SGreg Kroah-Hartman 	mk->kobj.kset = module_kset;
7791c7777feSShyam Saini 	err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL, "%s", name);
7801c7777feSShyam Saini 	if (IS_ENABLED(CONFIG_MODULES) && !err)
78188bfa324SKay Sievers 		err = sysfs_create_file(&mk->kobj, &module_uevent.attr);
7829b473de8SRusty Russell 	if (err) {
783e43b9192SGreg Kroah-Hartman 		kobject_put(&mk->kobj);
784b5f3abf9SJim Cromie 		pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",
785e94965edSDmitry Torokhov 			name, err);
786e94965edSDmitry Torokhov 		return NULL;
78774c5b597SGreg Kroah-Hartman 	}
788e94965edSDmitry Torokhov 
789e94965edSDmitry Torokhov 	/* So that we hold reference in both cases. */
7909b473de8SRusty Russell 	kobject_get(&mk->kobj);
7919b473de8SRusty Russell 
792e94965edSDmitry Torokhov 	return mk;
793e94965edSDmitry Torokhov }
794e94965edSDmitry Torokhov 
kernel_add_sysfs_param(const char * name,const struct kernel_param * kparam,unsigned int name_skip)795e94965edSDmitry Torokhov static void __init kernel_add_sysfs_param(const char *name,
79663a12d9dSGeert Uytterhoeven 					  const struct kernel_param *kparam,
797e94965edSDmitry Torokhov 					  unsigned int name_skip)
798e94965edSDmitry Torokhov {
799e94965edSDmitry Torokhov 	struct module_kobject *mk;
800e94965edSDmitry Torokhov 	int err;
801e94965edSDmitry Torokhov 
802bbc9462fSShyam Saini 	mk = lookup_or_create_module_kobject(name);
803e94965edSDmitry Torokhov 	if (!mk)
804e94965edSDmitry Torokhov 		return;
805e94965edSDmitry Torokhov 
806e94965edSDmitry Torokhov 	/* We need to remove old parameters before adding more. */
807e94965edSDmitry Torokhov 	if (mk->mp)
808e94965edSDmitry Torokhov 		sysfs_remove_group(&mk->kobj, &mk->mp->grp);
809e94965edSDmitry Torokhov 
8109b473de8SRusty Russell 	/* These should not fail at boot. */
8119b473de8SRusty Russell 	err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
8129b473de8SRusty Russell 	BUG_ON(err);
8139b473de8SRusty Russell 	err = sysfs_create_group(&mk->kobj, &mk->mp->grp);
8149b473de8SRusty Russell 	BUG_ON(err);
815f30c53a8SKay Sievers 	kobject_uevent(&mk->kobj, KOBJ_ADD);
8169b473de8SRusty Russell 	kobject_put(&mk->kobj);
8171da177e4SLinus Torvalds }
8181da177e4SLinus Torvalds 
8191da177e4SLinus Torvalds /*
820b634d130SJean Delvare  * param_sysfs_builtin - add sysfs parameters for built-in modules
8211da177e4SLinus Torvalds  *
8221da177e4SLinus Torvalds  * Add module_parameters to sysfs for "modules" built into the kernel.
8231da177e4SLinus Torvalds  *
8241da177e4SLinus Torvalds  * The "module" name (KBUILD_MODNAME) is stored before a dot, the
8251da177e4SLinus Torvalds  * "parameter" name is stored behind a dot in kernel_param->name. So,
8261da177e4SLinus Torvalds  * extract the "module" name for all built-in kernel_param-eters,
8279b473de8SRusty Russell  * and for all who have the same, call kernel_add_sysfs_param.
8281da177e4SLinus Torvalds  */
param_sysfs_builtin(void)8291da177e4SLinus Torvalds static void __init param_sysfs_builtin(void)
8301da177e4SLinus Torvalds {
83163a12d9dSGeert Uytterhoeven 	const struct kernel_param *kp;
8329b473de8SRusty Russell 	unsigned int name_len;
8339b473de8SRusty Russell 	char modname[MODULE_NAME_LEN];
8341da177e4SLinus Torvalds 
8359b473de8SRusty Russell 	for (kp = __start___param; kp < __stop___param; kp++) {
8361da177e4SLinus Torvalds 		char *dot;
8371da177e4SLinus Torvalds 
8389b473de8SRusty Russell 		if (kp->perm == 0)
8399b473de8SRusty Russell 			continue;
8401da177e4SLinus Torvalds 
841730b69d2SRusty Russell 		dot = strchr(kp->name, '.');
8421da177e4SLinus Torvalds 		if (!dot) {
84367e67ceaSRusty Russell 			/* This happens for core_param() */
84467e67ceaSRusty Russell 			strcpy(modname, "kernel");
84567e67ceaSRusty Russell 			name_len = 0;
84667e67ceaSRusty Russell 		} else {
84767e67ceaSRusty Russell 			name_len = dot - kp->name + 1;
84833457938SAzeem Shaikh 			strscpy(modname, kp->name, name_len);
8491da177e4SLinus Torvalds 		}
85067e67ceaSRusty Russell 		kernel_add_sysfs_param(modname, kp, name_len);
8511da177e4SLinus Torvalds 	}
8521da177e4SLinus Torvalds }
8531da177e4SLinus Torvalds 
__modver_version_show(const struct module_attribute * mattr,struct module_kobject * mk,char * buf)854f3227ffdSThomas Weißschuh ssize_t __modver_version_show(const struct module_attribute *mattr,
8554befb026SKay Sievers 			      struct module_kobject *mk, char *buf)
856e94965edSDmitry Torokhov {
85738e3fe65SThomas Weißschuh 	const struct module_version_attribute *vattr =
85838e3fe65SThomas Weißschuh 		container_of_const(mattr, struct module_version_attribute, mattr);
859e94965edSDmitry Torokhov 
860f4940ab7SChen Gang 	return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version);
861e94965edSDmitry Torokhov }
862e94965edSDmitry Torokhov 
863b112082cSJohan Hovold extern const struct module_version_attribute __start___modver[];
864b112082cSJohan Hovold extern const struct module_version_attribute __stop___modver[];
865e94965edSDmitry Torokhov 
version_sysfs_builtin(void)866e94965edSDmitry Torokhov static void __init version_sysfs_builtin(void)
867e94965edSDmitry Torokhov {
868b112082cSJohan Hovold 	const struct module_version_attribute *vattr;
869e94965edSDmitry Torokhov 	struct module_kobject *mk;
870e94965edSDmitry Torokhov 	int err;
871e94965edSDmitry Torokhov 
872b112082cSJohan Hovold 	for (vattr = __start___modver; vattr < __stop___modver; vattr++) {
873bbc9462fSShyam Saini 		mk = lookup_or_create_module_kobject(vattr->module_name);
874e94965edSDmitry Torokhov 		if (mk) {
875e94965edSDmitry Torokhov 			err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
87674c3dea3SRusty Russell 			WARN_ON_ONCE(err);
877e94965edSDmitry Torokhov 			kobject_uevent(&mk->kobj, KOBJ_ADD);
878e94965edSDmitry Torokhov 			kobject_put(&mk->kobj);
879e94965edSDmitry Torokhov 		}
880e94965edSDmitry Torokhov 	}
881e94965edSDmitry Torokhov }
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds /* module-related sysfs stuff */
8841da177e4SLinus Torvalds 
module_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)8851da177e4SLinus Torvalds static ssize_t module_attr_show(struct kobject *kobj,
8861da177e4SLinus Torvalds 				struct attribute *attr,
8871da177e4SLinus Torvalds 				char *buf)
8881da177e4SLinus Torvalds {
889f3227ffdSThomas Weißschuh 	const struct module_attribute *attribute;
8901da177e4SLinus Torvalds 	struct module_kobject *mk;
8911da177e4SLinus Torvalds 	int ret;
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds 	attribute = to_module_attr(attr);
8941da177e4SLinus Torvalds 	mk = to_module_kobject(kobj);
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 	if (!attribute->show)
89770f2817aSDmitry Torokhov 		return -EIO;
8981da177e4SLinus Torvalds 
8994befb026SKay Sievers 	ret = attribute->show(attribute, mk, buf);
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	return ret;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds 
module_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t len)9041da177e4SLinus Torvalds static ssize_t module_attr_store(struct kobject *kobj,
9051da177e4SLinus Torvalds 				struct attribute *attr,
9061da177e4SLinus Torvalds 				const char *buf, size_t len)
9071da177e4SLinus Torvalds {
908f3227ffdSThomas Weißschuh 	const struct module_attribute *attribute;
9091da177e4SLinus Torvalds 	struct module_kobject *mk;
9101da177e4SLinus Torvalds 	int ret;
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	attribute = to_module_attr(attr);
9131da177e4SLinus Torvalds 	mk = to_module_kobject(kobj);
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds 	if (!attribute->store)
91670f2817aSDmitry Torokhov 		return -EIO;
9171da177e4SLinus Torvalds 
9184befb026SKay Sievers 	ret = attribute->store(attribute, mk, buf, len);
9191da177e4SLinus Torvalds 
9201da177e4SLinus Torvalds 	return ret;
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds 
92352cf25d0SEmese Revfy static const struct sysfs_ops module_sysfs_ops = {
9241da177e4SLinus Torvalds 	.show = module_attr_show,
9251da177e4SLinus Torvalds 	.store = module_attr_store,
9261da177e4SLinus Torvalds };
9271da177e4SLinus Torvalds 
uevent_filter(const struct kobject * kobj)928c45a88bbSGreg Kroah-Hartman static int uevent_filter(const struct kobject *kobj)
929270a6c4cSKay Sievers {
930ee6d3dd4SWedson Almeida Filho 	const struct kobj_type *ktype = get_ktype(kobj);
931270a6c4cSKay Sievers 
932270a6c4cSKay Sievers 	if (ktype == &module_ktype)
933270a6c4cSKay Sievers 		return 1;
934270a6c4cSKay Sievers 	return 0;
935270a6c4cSKay Sievers }
936270a6c4cSKay Sievers 
9379cd43611SEmese Revfy static const struct kset_uevent_ops module_uevent_ops = {
938270a6c4cSKay Sievers 	.filter = uevent_filter,
939270a6c4cSKay Sievers };
940270a6c4cSKay Sievers 
9417405c1e1SGreg Kroah-Hartman struct kset *module_kset;
9421da177e4SLinus Torvalds 
module_kobj_release(struct kobject * kobj)943942e4431SLi Zhong static void module_kobj_release(struct kobject *kobj)
944942e4431SLi Zhong {
945942e4431SLi Zhong 	struct module_kobject *mk = to_module_kobject(kobj);
946*a6aeb739SDmitry Antipov 
947*a6aeb739SDmitry Antipov 	if (mk->kobj_completion)
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