1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * ipmi_si_hotmod.c 4 * 5 * Handling for dynamically adding/removing IPMI devices through 6 * a module parameter (and thus sysfs). 7 */ 8 #include <linux/moduleparam.h> 9 #include <linux/ipmi.h> 10 #include "ipmi_si.h" 11 12 #define PFX "ipmi_hotmod: " 13 14 static int hotmod_handler(const char *val, const struct kernel_param *kp); 15 16 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); 17 MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" 18 " Documentation/IPMI.txt in the kernel sources for the" 19 " gory details."); 20 21 /* 22 * Parms come in as <op1>[:op2[:op3...]]. ops are: 23 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] 24 * Options are: 25 * rsp=<regspacing> 26 * rsi=<regsize> 27 * rsh=<regshift> 28 * irq=<irq> 29 * ipmb=<ipmb addr> 30 */ 31 enum hotmod_op { HM_ADD, HM_REMOVE }; 32 struct hotmod_vals { 33 const char *name; 34 const int val; 35 }; 36 37 static const struct hotmod_vals hotmod_ops[] = { 38 { "add", HM_ADD }, 39 { "remove", HM_REMOVE }, 40 { NULL } 41 }; 42 43 static const struct hotmod_vals hotmod_si[] = { 44 { "kcs", SI_KCS }, 45 { "smic", SI_SMIC }, 46 { "bt", SI_BT }, 47 { NULL } 48 }; 49 50 static const struct hotmod_vals hotmod_as[] = { 51 { "mem", IPMI_MEM_ADDR_SPACE }, 52 { "i/o", IPMI_IO_ADDR_SPACE }, 53 { NULL } 54 }; 55 56 static int parse_str(const struct hotmod_vals *v, int *val, char *name, 57 char **curr) 58 { 59 char *s; 60 int i; 61 62 s = strchr(*curr, ','); 63 if (!s) { 64 pr_warn(PFX "No hotmod %s given.\n", name); 65 return -EINVAL; 66 } 67 *s = '\0'; 68 s++; 69 for (i = 0; v[i].name; i++) { 70 if (strcmp(*curr, v[i].name) == 0) { 71 *val = v[i].val; 72 *curr = s; 73 return 0; 74 } 75 } 76 77 pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); 78 return -EINVAL; 79 } 80 81 static int check_hotmod_int_op(const char *curr, const char *option, 82 const char *name, int *val) 83 { 84 char *n; 85 86 if (strcmp(curr, name) == 0) { 87 if (!option) { 88 pr_warn(PFX "No option given for '%s'\n", curr); 89 return -EINVAL; 90 } 91 *val = simple_strtoul(option, &n, 0); 92 if ((*n != '\0') || (*option == '\0')) { 93 pr_warn(PFX "Bad option given for '%s'\n", curr); 94 return -EINVAL; 95 } 96 return 1; 97 } 98 return 0; 99 } 100 101 static int hotmod_handler(const char *val, const struct kernel_param *kp) 102 { 103 char *str = kstrdup(val, GFP_KERNEL); 104 int rv; 105 char *next, *curr, *s, *n, *o; 106 enum hotmod_op op; 107 enum si_type si_type; 108 int addr_space; 109 unsigned long addr; 110 int regspacing; 111 int regsize; 112 int regshift; 113 int irq; 114 int ipmb; 115 int ival; 116 int len; 117 118 if (!str) 119 return -ENOMEM; 120 121 /* Kill any trailing spaces, as we can get a "\n" from echo. */ 122 len = strlen(str); 123 ival = len - 1; 124 while ((ival >= 0) && isspace(str[ival])) { 125 str[ival] = '\0'; 126 ival--; 127 } 128 129 for (curr = str; curr; curr = next) { 130 regspacing = 1; 131 regsize = 1; 132 regshift = 0; 133 irq = 0; 134 ipmb = 0; /* Choose the default if not specified */ 135 136 next = strchr(curr, ':'); 137 if (next) { 138 *next = '\0'; 139 next++; 140 } 141 142 rv = parse_str(hotmod_ops, &ival, "operation", &curr); 143 if (rv) 144 break; 145 op = ival; 146 147 rv = parse_str(hotmod_si, &ival, "interface type", &curr); 148 if (rv) 149 break; 150 si_type = ival; 151 152 rv = parse_str(hotmod_as, &addr_space, "address space", &curr); 153 if (rv) 154 break; 155 156 s = strchr(curr, ','); 157 if (s) { 158 *s = '\0'; 159 s++; 160 } 161 addr = simple_strtoul(curr, &n, 0); 162 if ((*n != '\0') || (*curr == '\0')) { 163 pr_warn(PFX "Invalid hotmod address '%s'\n", curr); 164 break; 165 } 166 167 while (s) { 168 curr = s; 169 s = strchr(curr, ','); 170 if (s) { 171 *s = '\0'; 172 s++; 173 } 174 o = strchr(curr, '='); 175 if (o) { 176 *o = '\0'; 177 o++; 178 } 179 rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); 180 if (rv < 0) 181 goto out; 182 else if (rv) 183 continue; 184 rv = check_hotmod_int_op(curr, o, "rsi", ®size); 185 if (rv < 0) 186 goto out; 187 else if (rv) 188 continue; 189 rv = check_hotmod_int_op(curr, o, "rsh", ®shift); 190 if (rv < 0) 191 goto out; 192 else if (rv) 193 continue; 194 rv = check_hotmod_int_op(curr, o, "irq", &irq); 195 if (rv < 0) 196 goto out; 197 else if (rv) 198 continue; 199 rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); 200 if (rv < 0) 201 goto out; 202 else if (rv) 203 continue; 204 205 rv = -EINVAL; 206 pr_warn(PFX "Invalid hotmod option '%s'\n", curr); 207 goto out; 208 } 209 210 if (op == HM_ADD) { 211 struct si_sm_io io; 212 213 memset(&io, 0, sizeof(io)); 214 io.addr_source = SI_HOTMOD; 215 io.si_type = si_type; 216 io.addr_data = addr; 217 io.addr_type = addr_space; 218 219 io.addr = NULL; 220 io.regspacing = regspacing; 221 if (!io.regspacing) 222 io.regspacing = DEFAULT_REGSPACING; 223 io.regsize = regsize; 224 if (!io.regsize) 225 io.regsize = DEFAULT_REGSIZE; 226 io.regshift = regshift; 227 io.irq = irq; 228 if (io.irq) 229 io.irq_setup = ipmi_std_irq_setup; 230 io.slave_addr = ipmb; 231 232 rv = ipmi_si_add_smi(&io); 233 if (rv) 234 goto out; 235 } else { 236 ipmi_si_remove_by_data(addr_space, si_type, addr); 237 } 238 } 239 rv = len; 240 out: 241 kfree(str); 242 return rv; 243 } 244