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 */ 8*25880f7dSJoe Perches 9*25880f7dSJoe Perches #define pr_fmt(fmt) "ipmi_hotmod: " fmt 10*25880f7dSJoe Perches 1144814ec9SCorey Minyard #include <linux/moduleparam.h> 1244814ec9SCorey Minyard #include <linux/ipmi.h> 1344814ec9SCorey Minyard #include "ipmi_si.h" 1444814ec9SCorey Minyard 156297fabdSCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp); 1644814ec9SCorey Minyard 1744814ec9SCorey Minyard module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); 1844814ec9SCorey Minyard MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" 1944814ec9SCorey Minyard " Documentation/IPMI.txt in the kernel sources for the" 2044814ec9SCorey Minyard " gory details."); 2144814ec9SCorey Minyard 2244814ec9SCorey Minyard /* 2344814ec9SCorey Minyard * Parms come in as <op1>[:op2[:op3...]]. ops are: 2444814ec9SCorey Minyard * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] 2544814ec9SCorey Minyard * Options are: 2644814ec9SCorey Minyard * rsp=<regspacing> 2744814ec9SCorey Minyard * rsi=<regsize> 2844814ec9SCorey Minyard * rsh=<regshift> 2944814ec9SCorey Minyard * irq=<irq> 3044814ec9SCorey Minyard * ipmb=<ipmb addr> 3144814ec9SCorey Minyard */ 3244814ec9SCorey Minyard enum hotmod_op { HM_ADD, HM_REMOVE }; 3344814ec9SCorey Minyard struct hotmod_vals { 3444814ec9SCorey Minyard const char *name; 3544814ec9SCorey Minyard const int val; 3644814ec9SCorey Minyard }; 3744814ec9SCorey Minyard 3844814ec9SCorey Minyard static const struct hotmod_vals hotmod_ops[] = { 3944814ec9SCorey Minyard { "add", HM_ADD }, 4044814ec9SCorey Minyard { "remove", HM_REMOVE }, 4144814ec9SCorey Minyard { NULL } 4244814ec9SCorey Minyard }; 4344814ec9SCorey Minyard 4444814ec9SCorey Minyard static const struct hotmod_vals hotmod_si[] = { 4544814ec9SCorey Minyard { "kcs", SI_KCS }, 4644814ec9SCorey Minyard { "smic", SI_SMIC }, 4744814ec9SCorey Minyard { "bt", SI_BT }, 4844814ec9SCorey Minyard { NULL } 4944814ec9SCorey Minyard }; 5044814ec9SCorey Minyard 5144814ec9SCorey Minyard static const struct hotmod_vals hotmod_as[] = { 5244814ec9SCorey Minyard { "mem", IPMI_MEM_ADDR_SPACE }, 5344814ec9SCorey Minyard { "i/o", IPMI_IO_ADDR_SPACE }, 5444814ec9SCorey Minyard { NULL } 5544814ec9SCorey Minyard }; 5644814ec9SCorey Minyard 5744814ec9SCorey Minyard static int parse_str(const struct hotmod_vals *v, int *val, char *name, 5844814ec9SCorey Minyard char **curr) 5944814ec9SCorey Minyard { 6044814ec9SCorey Minyard char *s; 6144814ec9SCorey Minyard int i; 6244814ec9SCorey Minyard 6344814ec9SCorey Minyard s = strchr(*curr, ','); 6444814ec9SCorey Minyard if (!s) { 65*25880f7dSJoe Perches pr_warn("No hotmod %s given\n", name); 6644814ec9SCorey Minyard return -EINVAL; 6744814ec9SCorey Minyard } 6844814ec9SCorey Minyard *s = '\0'; 6944814ec9SCorey Minyard s++; 7044814ec9SCorey Minyard for (i = 0; v[i].name; i++) { 7144814ec9SCorey Minyard if (strcmp(*curr, v[i].name) == 0) { 7244814ec9SCorey Minyard *val = v[i].val; 7344814ec9SCorey Minyard *curr = s; 7444814ec9SCorey Minyard return 0; 7544814ec9SCorey Minyard } 7644814ec9SCorey Minyard } 7744814ec9SCorey Minyard 78*25880f7dSJoe Perches pr_warn("Invalid hotmod %s '%s'\n", name, *curr); 7944814ec9SCorey Minyard return -EINVAL; 8044814ec9SCorey Minyard } 8144814ec9SCorey Minyard 8244814ec9SCorey Minyard static int check_hotmod_int_op(const char *curr, const char *option, 8344814ec9SCorey Minyard const char *name, int *val) 8444814ec9SCorey Minyard { 8544814ec9SCorey Minyard char *n; 8644814ec9SCorey Minyard 8744814ec9SCorey Minyard if (strcmp(curr, name) == 0) { 8844814ec9SCorey Minyard if (!option) { 89*25880f7dSJoe Perches pr_warn("No option given for '%s'\n", curr); 9044814ec9SCorey Minyard return -EINVAL; 9144814ec9SCorey Minyard } 9244814ec9SCorey Minyard *val = simple_strtoul(option, &n, 0); 9344814ec9SCorey Minyard if ((*n != '\0') || (*option == '\0')) { 94*25880f7dSJoe Perches pr_warn("Bad option given for '%s'\n", curr); 9544814ec9SCorey Minyard return -EINVAL; 9644814ec9SCorey Minyard } 9744814ec9SCorey Minyard return 1; 9844814ec9SCorey Minyard } 9944814ec9SCorey Minyard return 0; 10044814ec9SCorey Minyard } 10144814ec9SCorey Minyard 1026297fabdSCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp) 10344814ec9SCorey Minyard { 10444814ec9SCorey Minyard char *str = kstrdup(val, GFP_KERNEL); 10544814ec9SCorey Minyard int rv; 10644814ec9SCorey Minyard char *next, *curr, *s, *n, *o; 10744814ec9SCorey Minyard enum hotmod_op op; 10844814ec9SCorey Minyard enum si_type si_type; 10944814ec9SCorey Minyard int addr_space; 11044814ec9SCorey Minyard unsigned long addr; 11144814ec9SCorey Minyard int regspacing; 11244814ec9SCorey Minyard int regsize; 11344814ec9SCorey Minyard int regshift; 11444814ec9SCorey Minyard int irq; 11544814ec9SCorey Minyard int ipmb; 11644814ec9SCorey Minyard int ival; 11744814ec9SCorey Minyard int len; 11844814ec9SCorey Minyard 11944814ec9SCorey Minyard if (!str) 12044814ec9SCorey Minyard return -ENOMEM; 12144814ec9SCorey Minyard 12244814ec9SCorey Minyard /* Kill any trailing spaces, as we can get a "\n" from echo. */ 12344814ec9SCorey Minyard len = strlen(str); 12444814ec9SCorey Minyard ival = len - 1; 12544814ec9SCorey Minyard while ((ival >= 0) && isspace(str[ival])) { 12644814ec9SCorey Minyard str[ival] = '\0'; 12744814ec9SCorey Minyard ival--; 12844814ec9SCorey Minyard } 12944814ec9SCorey Minyard 13044814ec9SCorey Minyard for (curr = str; curr; curr = next) { 13144814ec9SCorey Minyard regspacing = 1; 13244814ec9SCorey Minyard regsize = 1; 13344814ec9SCorey Minyard regshift = 0; 13444814ec9SCorey Minyard irq = 0; 13544814ec9SCorey Minyard ipmb = 0; /* Choose the default if not specified */ 13644814ec9SCorey Minyard 13744814ec9SCorey Minyard next = strchr(curr, ':'); 13844814ec9SCorey Minyard if (next) { 13944814ec9SCorey Minyard *next = '\0'; 14044814ec9SCorey Minyard next++; 14144814ec9SCorey Minyard } 14244814ec9SCorey Minyard 14344814ec9SCorey Minyard rv = parse_str(hotmod_ops, &ival, "operation", &curr); 14444814ec9SCorey Minyard if (rv) 14544814ec9SCorey Minyard break; 14644814ec9SCorey Minyard op = ival; 14744814ec9SCorey Minyard 14844814ec9SCorey Minyard rv = parse_str(hotmod_si, &ival, "interface type", &curr); 14944814ec9SCorey Minyard if (rv) 15044814ec9SCorey Minyard break; 15144814ec9SCorey Minyard si_type = ival; 15244814ec9SCorey Minyard 15344814ec9SCorey Minyard rv = parse_str(hotmod_as, &addr_space, "address space", &curr); 15444814ec9SCorey Minyard if (rv) 15544814ec9SCorey Minyard break; 15644814ec9SCorey Minyard 15744814ec9SCorey Minyard s = strchr(curr, ','); 15844814ec9SCorey Minyard if (s) { 15944814ec9SCorey Minyard *s = '\0'; 16044814ec9SCorey Minyard s++; 16144814ec9SCorey Minyard } 16244814ec9SCorey Minyard addr = simple_strtoul(curr, &n, 0); 16344814ec9SCorey Minyard if ((*n != '\0') || (*curr == '\0')) { 164*25880f7dSJoe Perches pr_warn("Invalid hotmod address '%s'\n", curr); 16544814ec9SCorey Minyard break; 16644814ec9SCorey Minyard } 16744814ec9SCorey Minyard 16844814ec9SCorey Minyard while (s) { 16944814ec9SCorey Minyard curr = s; 17044814ec9SCorey Minyard s = strchr(curr, ','); 17144814ec9SCorey Minyard if (s) { 17244814ec9SCorey Minyard *s = '\0'; 17344814ec9SCorey Minyard s++; 17444814ec9SCorey Minyard } 17544814ec9SCorey Minyard o = strchr(curr, '='); 17644814ec9SCorey Minyard if (o) { 17744814ec9SCorey Minyard *o = '\0'; 17844814ec9SCorey Minyard o++; 17944814ec9SCorey Minyard } 18044814ec9SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); 18144814ec9SCorey Minyard if (rv < 0) 18244814ec9SCorey Minyard goto out; 18344814ec9SCorey Minyard else if (rv) 18444814ec9SCorey Minyard continue; 18544814ec9SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsi", ®size); 18644814ec9SCorey Minyard if (rv < 0) 18744814ec9SCorey Minyard goto out; 18844814ec9SCorey Minyard else if (rv) 18944814ec9SCorey Minyard continue; 19044814ec9SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsh", ®shift); 19144814ec9SCorey Minyard if (rv < 0) 19244814ec9SCorey Minyard goto out; 19344814ec9SCorey Minyard else if (rv) 19444814ec9SCorey Minyard continue; 19544814ec9SCorey Minyard rv = check_hotmod_int_op(curr, o, "irq", &irq); 19644814ec9SCorey Minyard if (rv < 0) 19744814ec9SCorey Minyard goto out; 19844814ec9SCorey Minyard else if (rv) 19944814ec9SCorey Minyard continue; 20044814ec9SCorey Minyard rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); 20144814ec9SCorey Minyard if (rv < 0) 20244814ec9SCorey Minyard goto out; 20344814ec9SCorey Minyard else if (rv) 20444814ec9SCorey Minyard continue; 20544814ec9SCorey Minyard 20644814ec9SCorey Minyard rv = -EINVAL; 207*25880f7dSJoe Perches pr_warn("Invalid hotmod option '%s'\n", curr); 20844814ec9SCorey Minyard goto out; 20944814ec9SCorey Minyard } 21044814ec9SCorey Minyard 21144814ec9SCorey Minyard if (op == HM_ADD) { 21244814ec9SCorey Minyard struct si_sm_io io; 21344814ec9SCorey Minyard 21444814ec9SCorey Minyard memset(&io, 0, sizeof(io)); 21544814ec9SCorey Minyard io.addr_source = SI_HOTMOD; 21644814ec9SCorey Minyard io.si_type = si_type; 21744814ec9SCorey Minyard io.addr_data = addr; 21844814ec9SCorey Minyard io.addr_type = addr_space; 21944814ec9SCorey Minyard 22044814ec9SCorey Minyard io.addr = NULL; 22144814ec9SCorey Minyard io.regspacing = regspacing; 22244814ec9SCorey Minyard if (!io.regspacing) 22344814ec9SCorey Minyard io.regspacing = DEFAULT_REGSPACING; 22444814ec9SCorey Minyard io.regsize = regsize; 22544814ec9SCorey Minyard if (!io.regsize) 22644814ec9SCorey Minyard io.regsize = DEFAULT_REGSIZE; 22744814ec9SCorey Minyard io.regshift = regshift; 22844814ec9SCorey Minyard io.irq = irq; 22944814ec9SCorey Minyard if (io.irq) 23044814ec9SCorey Minyard io.irq_setup = ipmi_std_irq_setup; 23144814ec9SCorey Minyard io.slave_addr = ipmb; 23244814ec9SCorey Minyard 23344814ec9SCorey Minyard rv = ipmi_si_add_smi(&io); 23444814ec9SCorey Minyard if (rv) 23544814ec9SCorey Minyard goto out; 23644814ec9SCorey Minyard } else { 23744814ec9SCorey Minyard ipmi_si_remove_by_data(addr_space, si_type, addr); 23844814ec9SCorey Minyard } 23944814ec9SCorey Minyard } 24044814ec9SCorey Minyard rv = len; 24144814ec9SCorey Minyard out: 24244814ec9SCorey Minyard kfree(str); 24344814ec9SCorey Minyard return rv; 24444814ec9SCorey Minyard } 245