0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "ipmi_hotmod: " fmt
0010
0011 #include <linux/moduleparam.h>
0012 #include <linux/ipmi.h>
0013 #include <linux/atomic.h>
0014 #include "ipmi_si.h"
0015 #include "ipmi_plat_data.h"
0016
0017 static int hotmod_handler(const char *val, const struct kernel_param *kp);
0018
0019 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
0020 MODULE_PARM_DESC(hotmod,
0021 "Add and remove interfaces. See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 enum hotmod_op { HM_ADD, HM_REMOVE };
0034 struct hotmod_vals {
0035 const char *name;
0036 const int val;
0037 };
0038
0039 static const struct hotmod_vals hotmod_ops[] = {
0040 { "add", HM_ADD },
0041 { "remove", HM_REMOVE },
0042 { NULL }
0043 };
0044
0045 static const struct hotmod_vals hotmod_si[] = {
0046 { "kcs", SI_KCS },
0047 { "smic", SI_SMIC },
0048 { "bt", SI_BT },
0049 { NULL }
0050 };
0051
0052 static const struct hotmod_vals hotmod_as[] = {
0053 { "mem", IPMI_MEM_ADDR_SPACE },
0054 { "i/o", IPMI_IO_ADDR_SPACE },
0055 { NULL }
0056 };
0057
0058 static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
0059 const char **curr)
0060 {
0061 char *s;
0062 int i;
0063
0064 s = strchr(*curr, ',');
0065 if (!s) {
0066 pr_warn("No hotmod %s given\n", name);
0067 return -EINVAL;
0068 }
0069 *s = '\0';
0070 s++;
0071 for (i = 0; v[i].name; i++) {
0072 if (strcmp(*curr, v[i].name) == 0) {
0073 *val = v[i].val;
0074 *curr = s;
0075 return 0;
0076 }
0077 }
0078
0079 pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
0080 return -EINVAL;
0081 }
0082
0083 static int check_hotmod_int_op(const char *curr, const char *option,
0084 const char *name, unsigned int *val)
0085 {
0086 char *n;
0087
0088 if (strcmp(curr, name) == 0) {
0089 if (!option) {
0090 pr_warn("No option given for '%s'\n", curr);
0091 return -EINVAL;
0092 }
0093 *val = simple_strtoul(option, &n, 0);
0094 if ((*n != '\0') || (*option == '\0')) {
0095 pr_warn("Bad option given for '%s'\n", curr);
0096 return -EINVAL;
0097 }
0098 return 1;
0099 }
0100 return 0;
0101 }
0102
0103 static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
0104 struct ipmi_plat_data *h)
0105 {
0106 char *s, *o;
0107 int rv;
0108 unsigned int ival;
0109
0110 h->iftype = IPMI_PLAT_IF_SI;
0111 rv = parse_str(hotmod_ops, &ival, "operation", &curr);
0112 if (rv)
0113 return rv;
0114 *op = ival;
0115
0116 rv = parse_str(hotmod_si, &ival, "interface type", &curr);
0117 if (rv)
0118 return rv;
0119 h->type = ival;
0120
0121 rv = parse_str(hotmod_as, &ival, "address space", &curr);
0122 if (rv)
0123 return rv;
0124 h->space = ival;
0125
0126 s = strchr(curr, ',');
0127 if (s) {
0128 *s = '\0';
0129 s++;
0130 }
0131 rv = kstrtoul(curr, 0, &h->addr);
0132 if (rv) {
0133 pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
0134 return rv;
0135 }
0136
0137 while (s) {
0138 curr = s;
0139 s = strchr(curr, ',');
0140 if (s) {
0141 *s = '\0';
0142 s++;
0143 }
0144 o = strchr(curr, '=');
0145 if (o) {
0146 *o = '\0';
0147 o++;
0148 }
0149 rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
0150 if (rv < 0)
0151 return rv;
0152 else if (rv)
0153 continue;
0154 rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
0155 if (rv < 0)
0156 return rv;
0157 else if (rv)
0158 continue;
0159 rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
0160 if (rv < 0)
0161 return rv;
0162 else if (rv)
0163 continue;
0164 rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
0165 if (rv < 0)
0166 return rv;
0167 else if (rv)
0168 continue;
0169 rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
0170 if (rv < 0)
0171 return rv;
0172 else if (rv)
0173 continue;
0174
0175 pr_warn("Invalid hotmod option '%s'\n", curr);
0176 return -EINVAL;
0177 }
0178
0179 h->addr_source = SI_HOTMOD;
0180 return 0;
0181 }
0182
0183 static atomic_t hotmod_nr;
0184
0185 static int hotmod_handler(const char *val, const struct kernel_param *kp)
0186 {
0187 int rv;
0188 struct ipmi_plat_data h;
0189 char *str, *curr, *next;
0190
0191 str = kstrdup(val, GFP_KERNEL);
0192 if (!str)
0193 return -ENOMEM;
0194
0195
0196 for (curr = strstrip(str); curr; curr = next) {
0197 enum hotmod_op op;
0198
0199 next = strchr(curr, ':');
0200 if (next) {
0201 *next = '\0';
0202 next++;
0203 }
0204
0205 memset(&h, 0, sizeof(h));
0206 rv = parse_hotmod_str(curr, &op, &h);
0207 if (rv)
0208 goto out;
0209
0210 if (op == HM_ADD) {
0211 ipmi_platform_add("hotmod-ipmi-si",
0212 atomic_inc_return(&hotmod_nr),
0213 &h);
0214 } else {
0215 struct device *dev;
0216
0217 dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
0218 if (dev && dev_is_platform(dev)) {
0219 struct platform_device *pdev;
0220
0221 pdev = to_platform_device(dev);
0222 if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
0223 platform_device_unregister(pdev);
0224 }
0225 put_device(dev);
0226 }
0227 }
0228 rv = strlen(val);
0229 out:
0230 kfree(str);
0231 return rv;
0232 }
0233
0234 void ipmi_si_hotmod_exit(void)
0235 {
0236 ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
0237 }