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