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