xref: /openbmc/linux/drivers/char/ipmi/ipmi_si_hotmod.c (revision 3bb8ea400cbe2ae4d5b51e4306ff9d14d6c6627c)
1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+
244814ec9SCorey Minyard /*
344814ec9SCorey Minyard  * ipmi_si_hotmod.c
444814ec9SCorey Minyard  *
544814ec9SCorey Minyard  * Handling for dynamically adding/removing IPMI devices through
644814ec9SCorey Minyard  * a module parameter (and thus sysfs).
744814ec9SCorey Minyard  */
825880f7dSJoe Perches 
925880f7dSJoe Perches #define pr_fmt(fmt) "ipmi_hotmod: " fmt
1025880f7dSJoe Perches 
1144814ec9SCorey Minyard #include <linux/moduleparam.h>
1244814ec9SCorey Minyard #include <linux/ipmi.h>
13*3bb8ea40SCorey Minyard #include <linux/atomic.h>
1444814ec9SCorey Minyard #include "ipmi_si.h"
15*3bb8ea40SCorey Minyard #include "ipmi_plat_data.h"
1644814ec9SCorey Minyard 
176297fabdSCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp);
1844814ec9SCorey Minyard 
1944814ec9SCorey Minyard module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
2044814ec9SCorey Minyard MODULE_PARM_DESC(hotmod, "Add and remove interfaces.  See"
2144814ec9SCorey Minyard 		 " Documentation/IPMI.txt in the kernel sources for the"
2244814ec9SCorey Minyard 		 " gory details.");
2344814ec9SCorey Minyard 
2444814ec9SCorey Minyard /*
2544814ec9SCorey Minyard  * Parms come in as <op1>[:op2[:op3...]].  ops are:
2644814ec9SCorey Minyard  *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
2744814ec9SCorey Minyard  * Options are:
2844814ec9SCorey Minyard  *   rsp=<regspacing>
2944814ec9SCorey Minyard  *   rsi=<regsize>
3044814ec9SCorey Minyard  *   rsh=<regshift>
3144814ec9SCorey Minyard  *   irq=<irq>
3244814ec9SCorey Minyard  *   ipmb=<ipmb addr>
3344814ec9SCorey Minyard  */
3444814ec9SCorey Minyard enum hotmod_op { HM_ADD, HM_REMOVE };
3544814ec9SCorey Minyard struct hotmod_vals {
3644814ec9SCorey Minyard 	const char *name;
3744814ec9SCorey Minyard 	const int  val;
3844814ec9SCorey Minyard };
3944814ec9SCorey Minyard 
4044814ec9SCorey Minyard static const struct hotmod_vals hotmod_ops[] = {
4144814ec9SCorey Minyard 	{ "add",	HM_ADD },
4244814ec9SCorey Minyard 	{ "remove",	HM_REMOVE },
4344814ec9SCorey Minyard 	{ NULL }
4444814ec9SCorey Minyard };
4544814ec9SCorey Minyard 
4644814ec9SCorey Minyard static const struct hotmod_vals hotmod_si[] = {
4744814ec9SCorey Minyard 	{ "kcs",	SI_KCS },
4844814ec9SCorey Minyard 	{ "smic",	SI_SMIC },
4944814ec9SCorey Minyard 	{ "bt",		SI_BT },
5044814ec9SCorey Minyard 	{ NULL }
5144814ec9SCorey Minyard };
5244814ec9SCorey Minyard 
5344814ec9SCorey Minyard static const struct hotmod_vals hotmod_as[] = {
5444814ec9SCorey Minyard 	{ "mem",	IPMI_MEM_ADDR_SPACE },
5544814ec9SCorey Minyard 	{ "i/o",	IPMI_IO_ADDR_SPACE },
5644814ec9SCorey Minyard 	{ NULL }
5744814ec9SCorey Minyard };
5844814ec9SCorey Minyard 
59*3bb8ea40SCorey Minyard static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
60*3bb8ea40SCorey Minyard 		     const char **curr)
6144814ec9SCorey Minyard {
6244814ec9SCorey Minyard 	char *s;
6344814ec9SCorey Minyard 	int  i;
6444814ec9SCorey Minyard 
6544814ec9SCorey Minyard 	s = strchr(*curr, ',');
6644814ec9SCorey Minyard 	if (!s) {
6725880f7dSJoe Perches 		pr_warn("No hotmod %s given\n", name);
6844814ec9SCorey Minyard 		return -EINVAL;
6944814ec9SCorey Minyard 	}
7044814ec9SCorey Minyard 	*s = '\0';
7144814ec9SCorey Minyard 	s++;
7244814ec9SCorey Minyard 	for (i = 0; v[i].name; i++) {
7344814ec9SCorey Minyard 		if (strcmp(*curr, v[i].name) == 0) {
7444814ec9SCorey Minyard 			*val = v[i].val;
7544814ec9SCorey Minyard 			*curr = s;
7644814ec9SCorey Minyard 			return 0;
7744814ec9SCorey Minyard 		}
7844814ec9SCorey Minyard 	}
7944814ec9SCorey Minyard 
8025880f7dSJoe Perches 	pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
8144814ec9SCorey Minyard 	return -EINVAL;
8244814ec9SCorey Minyard }
8344814ec9SCorey Minyard 
8444814ec9SCorey Minyard static int check_hotmod_int_op(const char *curr, const char *option,
85*3bb8ea40SCorey Minyard 			       const char *name, unsigned int *val)
8644814ec9SCorey Minyard {
8744814ec9SCorey Minyard 	char *n;
8844814ec9SCorey Minyard 
8944814ec9SCorey Minyard 	if (strcmp(curr, name) == 0) {
9044814ec9SCorey Minyard 		if (!option) {
9125880f7dSJoe Perches 			pr_warn("No option given for '%s'\n", curr);
9244814ec9SCorey Minyard 			return -EINVAL;
9344814ec9SCorey Minyard 		}
9444814ec9SCorey Minyard 		*val = simple_strtoul(option, &n, 0);
9544814ec9SCorey Minyard 		if ((*n != '\0') || (*option == '\0')) {
9625880f7dSJoe Perches 			pr_warn("Bad option given for '%s'\n", curr);
9744814ec9SCorey Minyard 			return -EINVAL;
9844814ec9SCorey Minyard 		}
9944814ec9SCorey Minyard 		return 1;
10044814ec9SCorey Minyard 	}
10144814ec9SCorey Minyard 	return 0;
10244814ec9SCorey Minyard }
10344814ec9SCorey Minyard 
104*3bb8ea40SCorey Minyard static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
105*3bb8ea40SCorey Minyard 			    struct ipmi_plat_data *h)
10644814ec9SCorey Minyard {
107*3bb8ea40SCorey Minyard 	char *s, *o;
10844814ec9SCorey Minyard 	int rv;
109*3bb8ea40SCorey Minyard 	unsigned int ival;
11044814ec9SCorey Minyard 
11144814ec9SCorey Minyard 	rv = parse_str(hotmod_ops, &ival, "operation", &curr);
11244814ec9SCorey Minyard 	if (rv)
113*3bb8ea40SCorey Minyard 		return rv;
114*3bb8ea40SCorey Minyard 	*op = ival;
11544814ec9SCorey Minyard 
11644814ec9SCorey Minyard 	rv = parse_str(hotmod_si, &ival, "interface type", &curr);
11744814ec9SCorey Minyard 	if (rv)
118*3bb8ea40SCorey Minyard 		return rv;
119*3bb8ea40SCorey Minyard 	h->type = ival;
12044814ec9SCorey Minyard 
121f6296bdcSCorey Minyard 	rv = parse_str(hotmod_as, &ival, "address space", &curr);
12244814ec9SCorey Minyard 	if (rv)
123*3bb8ea40SCorey Minyard 		return rv;
124*3bb8ea40SCorey Minyard 	h->space = ival;
12544814ec9SCorey Minyard 
12644814ec9SCorey Minyard 	s = strchr(curr, ',');
12744814ec9SCorey Minyard 	if (s) {
12844814ec9SCorey Minyard 		*s = '\0';
12944814ec9SCorey Minyard 		s++;
13044814ec9SCorey Minyard 	}
131*3bb8ea40SCorey Minyard 	rv = kstrtoul(curr, 0, &h->addr);
132*3bb8ea40SCorey Minyard 	if (rv) {
133*3bb8ea40SCorey Minyard 		pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
134*3bb8ea40SCorey Minyard 		return rv;
13544814ec9SCorey Minyard 	}
13644814ec9SCorey Minyard 
13744814ec9SCorey Minyard 	while (s) {
13844814ec9SCorey Minyard 		curr = s;
13944814ec9SCorey Minyard 		s = strchr(curr, ',');
14044814ec9SCorey Minyard 		if (s) {
14144814ec9SCorey Minyard 			*s = '\0';
14244814ec9SCorey Minyard 			s++;
14344814ec9SCorey Minyard 		}
14444814ec9SCorey Minyard 		o = strchr(curr, '=');
14544814ec9SCorey Minyard 		if (o) {
14644814ec9SCorey Minyard 			*o = '\0';
14744814ec9SCorey Minyard 			o++;
14844814ec9SCorey Minyard 		}
149*3bb8ea40SCorey Minyard 		rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
15044814ec9SCorey Minyard 		if (rv < 0)
151*3bb8ea40SCorey Minyard 			return rv;
15244814ec9SCorey Minyard 		else if (rv)
15344814ec9SCorey Minyard 			continue;
154*3bb8ea40SCorey Minyard 		rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
15544814ec9SCorey Minyard 		if (rv < 0)
156*3bb8ea40SCorey Minyard 			return rv;
15744814ec9SCorey Minyard 		else if (rv)
15844814ec9SCorey Minyard 			continue;
159*3bb8ea40SCorey Minyard 		rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
16044814ec9SCorey Minyard 		if (rv < 0)
161*3bb8ea40SCorey Minyard 			return rv;
16244814ec9SCorey Minyard 		else if (rv)
16344814ec9SCorey Minyard 			continue;
164*3bb8ea40SCorey Minyard 		rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
16544814ec9SCorey Minyard 		if (rv < 0)
166*3bb8ea40SCorey Minyard 			return rv;
16744814ec9SCorey Minyard 		else if (rv)
16844814ec9SCorey Minyard 			continue;
169*3bb8ea40SCorey Minyard 		rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
17044814ec9SCorey Minyard 		if (rv < 0)
171*3bb8ea40SCorey Minyard 			return rv;
17244814ec9SCorey Minyard 		else if (rv)
17344814ec9SCorey Minyard 			continue;
17444814ec9SCorey Minyard 
17525880f7dSJoe Perches 		pr_warn("Invalid hotmod option '%s'\n", curr);
176*3bb8ea40SCorey Minyard 		return -EINVAL;
17744814ec9SCorey Minyard 	}
17844814ec9SCorey Minyard 
179*3bb8ea40SCorey Minyard 	h->addr_source = SI_HOTMOD;
180*3bb8ea40SCorey Minyard 	return 0;
181*3bb8ea40SCorey Minyard }
18244814ec9SCorey Minyard 
183*3bb8ea40SCorey Minyard static atomic_t hotmod_nr;
18444814ec9SCorey Minyard 
185*3bb8ea40SCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp)
186*3bb8ea40SCorey Minyard {
187*3bb8ea40SCorey Minyard 	char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
188*3bb8ea40SCorey Minyard 	int  rv;
189*3bb8ea40SCorey Minyard 	struct ipmi_plat_data h;
190*3bb8ea40SCorey Minyard 	unsigned int len, ival;
19144814ec9SCorey Minyard 
192*3bb8ea40SCorey Minyard 	if (!str)
193*3bb8ea40SCorey Minyard 		return -ENOMEM;
194*3bb8ea40SCorey Minyard 
195*3bb8ea40SCorey Minyard 	/* Kill any trailing spaces, as we can get a "\n" from echo. */
196*3bb8ea40SCorey Minyard 	len = strlen(str);
197*3bb8ea40SCorey Minyard 	ival = len - 1;
198*3bb8ea40SCorey Minyard 	while ((ival >= 0) && isspace(str[ival])) {
199*3bb8ea40SCorey Minyard 		str[ival] = '\0';
200*3bb8ea40SCorey Minyard 		ival--;
201*3bb8ea40SCorey Minyard 	}
202*3bb8ea40SCorey Minyard 
203*3bb8ea40SCorey Minyard 	for (curr = str; curr; curr = next) {
204*3bb8ea40SCorey Minyard 		enum hotmod_op op;
205*3bb8ea40SCorey Minyard 
206*3bb8ea40SCorey Minyard 		next = strchr(curr, ':');
207*3bb8ea40SCorey Minyard 		if (next) {
208*3bb8ea40SCorey Minyard 			*next = '\0';
209*3bb8ea40SCorey Minyard 			next++;
210*3bb8ea40SCorey Minyard 		}
211*3bb8ea40SCorey Minyard 
212*3bb8ea40SCorey Minyard 		memset(&h, 0, sizeof(h));
213*3bb8ea40SCorey Minyard 		rv = parse_hotmod_str(curr, &op, &h);
21444814ec9SCorey Minyard 		if (rv)
21544814ec9SCorey Minyard 			goto out;
216*3bb8ea40SCorey Minyard 
217*3bb8ea40SCorey Minyard 		if (op == HM_ADD) {
218*3bb8ea40SCorey Minyard 			ipmi_platform_add("hotmod-ipmi-si",
219*3bb8ea40SCorey Minyard 					  atomic_inc_return(&hotmod_nr),
220*3bb8ea40SCorey Minyard 					  &h);
22144814ec9SCorey Minyard 		} else {
222*3bb8ea40SCorey Minyard 			ipmi_si_remove_by_data(h.space, h.type, h.addr);
22344814ec9SCorey Minyard 		}
22444814ec9SCorey Minyard 	}
22544814ec9SCorey Minyard 	rv = len;
22644814ec9SCorey Minyard out:
22744814ec9SCorey Minyard 	kfree(str);
22844814ec9SCorey Minyard 	return rv;
22944814ec9SCorey Minyard }
230