Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/pseries/reconfig.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
4
* Hotplug and Dynamic Logical Partitioning on RPA platforms).
5
*
6
* Copyright (C) 2005 Nathan Lynch
7
* Copyright (C) 2005 IBM Corporation
8
*/
9
10
#include <linux/kernel.h>
11
#include <linux/notifier.h>
12
#include <linux/proc_fs.h>
13
#include <linux/security.h>
14
#include <linux/slab.h>
15
#include <linux/of.h>
16
17
#include <asm/machdep.h>
18
#include <linux/uaccess.h>
19
#include <asm/mmu.h>
20
21
#include "of_helpers.h"
22
23
static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
24
{
25
struct device_node *np;
26
int err = -ENOMEM;
27
28
np = kzalloc(sizeof(*np), GFP_KERNEL);
29
if (!np)
30
goto out_err;
31
32
np->full_name = kstrdup(kbasename(path), GFP_KERNEL);
33
if (!np->full_name)
34
goto out_err;
35
36
np->properties = proplist;
37
of_node_set_flag(np, OF_DYNAMIC);
38
of_node_init(np);
39
40
np->parent = pseries_of_derive_parent(path);
41
if (IS_ERR(np->parent)) {
42
err = PTR_ERR(np->parent);
43
goto out_err;
44
}
45
46
err = of_attach_node(np);
47
if (err) {
48
printk(KERN_ERR "Failed to add device node %s\n", path);
49
goto out_err;
50
}
51
52
of_node_put(np->parent);
53
54
return 0;
55
56
out_err:
57
if (np) {
58
of_node_put(np->parent);
59
kfree(np->full_name);
60
kfree(np);
61
}
62
return err;
63
}
64
65
static int pSeries_reconfig_remove_node(struct device_node *np)
66
{
67
struct device_node *parent, *child;
68
69
parent = of_get_parent(np);
70
if (!parent)
71
return -EINVAL;
72
73
if ((child = of_get_next_child(np, NULL))) {
74
of_node_put(child);
75
of_node_put(parent);
76
return -EBUSY;
77
}
78
79
of_detach_node(np);
80
of_node_put(parent);
81
return 0;
82
}
83
84
/*
85
* /proc/powerpc/ofdt - yucky binary interface for adding and removing
86
* OF device nodes. Should be deprecated as soon as we get an
87
* in-kernel wrapper for the RTAS ibm,configure-connector call.
88
*/
89
90
static void release_prop_list(const struct property *prop)
91
{
92
struct property *next;
93
for (; prop; prop = next) {
94
next = prop->next;
95
kfree(prop->name);
96
kfree(prop->value);
97
kfree(prop);
98
}
99
100
}
101
102
/**
103
* parse_next_property - process the next property from raw input buffer
104
* @buf: input buffer, must be nul-terminated
105
* @end: end of the input buffer + 1, for validation
106
* @name: return value; set to property name in buf
107
* @length: return value; set to length of value
108
* @value: return value; set to the property value in buf
109
*
110
* Note that the caller must make copies of the name and value returned,
111
* this function does no allocation or copying of the data. Return value
112
* is set to the next name in buf, or NULL on error.
113
*/
114
static char * parse_next_property(char *buf, char *end, char **name, int *length,
115
unsigned char **value)
116
{
117
char *tmp;
118
119
*name = buf;
120
121
tmp = strchr(buf, ' ');
122
if (!tmp) {
123
printk(KERN_ERR "property parse failed in %s at line %d\n",
124
__func__, __LINE__);
125
return NULL;
126
}
127
*tmp = '\0';
128
129
if (++tmp >= end) {
130
printk(KERN_ERR "property parse failed in %s at line %d\n",
131
__func__, __LINE__);
132
return NULL;
133
}
134
135
/* now we're on the length */
136
*length = -1;
137
*length = simple_strtoul(tmp, &tmp, 10);
138
if (*length == -1) {
139
printk(KERN_ERR "property parse failed in %s at line %d\n",
140
__func__, __LINE__);
141
return NULL;
142
}
143
if (*tmp != ' ' || ++tmp >= end) {
144
printk(KERN_ERR "property parse failed in %s at line %d\n",
145
__func__, __LINE__);
146
return NULL;
147
}
148
149
/* now we're on the value */
150
*value = tmp;
151
tmp += *length;
152
if (tmp > end) {
153
printk(KERN_ERR "property parse failed in %s at line %d\n",
154
__func__, __LINE__);
155
return NULL;
156
}
157
else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
158
printk(KERN_ERR "property parse failed in %s at line %d\n",
159
__func__, __LINE__);
160
return NULL;
161
}
162
tmp++;
163
164
/* and now we should be on the next name, or the end */
165
return tmp;
166
}
167
168
static struct property *new_property(const char *name, const int length,
169
const unsigned char *value, struct property *last)
170
{
171
struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
172
173
if (!new)
174
return NULL;
175
176
if (!(new->name = kstrdup(name, GFP_KERNEL)))
177
goto cleanup;
178
if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
179
goto cleanup;
180
181
memcpy(new->value, value, length);
182
*(((char *)new->value) + length) = 0;
183
new->length = length;
184
new->next = last;
185
return new;
186
187
cleanup:
188
kfree(new->name);
189
kfree(new->value);
190
kfree(new);
191
return NULL;
192
}
193
194
static int do_add_node(char *buf, size_t bufsize)
195
{
196
char *path, *end, *name;
197
struct device_node *np;
198
struct property *prop = NULL;
199
unsigned char* value;
200
int length, rv = 0;
201
202
end = buf + bufsize;
203
path = buf;
204
buf = strchr(buf, ' ');
205
if (!buf)
206
return -EINVAL;
207
*buf = '\0';
208
buf++;
209
210
if ((np = of_find_node_by_path(path))) {
211
of_node_put(np);
212
return -EINVAL;
213
}
214
215
/* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
216
while (buf < end &&
217
(buf = parse_next_property(buf, end, &name, &length, &value))) {
218
struct property *last = prop;
219
220
prop = new_property(name, length, value, last);
221
if (!prop) {
222
rv = -ENOMEM;
223
prop = last;
224
goto out;
225
}
226
}
227
if (!buf) {
228
rv = -EINVAL;
229
goto out;
230
}
231
232
rv = pSeries_reconfig_add_node(path, prop);
233
234
out:
235
if (rv)
236
release_prop_list(prop);
237
return rv;
238
}
239
240
static int do_remove_node(char *buf)
241
{
242
struct device_node *node;
243
int rv = -ENODEV;
244
245
if ((node = of_find_node_by_path(buf)))
246
rv = pSeries_reconfig_remove_node(node);
247
248
of_node_put(node);
249
return rv;
250
}
251
252
static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
253
{
254
char *handle_str;
255
phandle handle;
256
*npp = NULL;
257
258
handle_str = buf;
259
260
buf = strchr(buf, ' ');
261
if (!buf)
262
return NULL;
263
*buf = '\0';
264
buf++;
265
266
handle = simple_strtoul(handle_str, NULL, 0);
267
268
*npp = of_find_node_by_phandle(handle);
269
return buf;
270
}
271
272
static int do_add_property(char *buf, size_t bufsize)
273
{
274
struct property *prop = NULL;
275
struct device_node *np;
276
unsigned char *value;
277
char *name, *end;
278
int length;
279
end = buf + bufsize;
280
buf = parse_node(buf, bufsize, &np);
281
282
if (!np)
283
return -ENODEV;
284
285
if (parse_next_property(buf, end, &name, &length, &value) == NULL)
286
return -EINVAL;
287
288
prop = new_property(name, length, value, NULL);
289
if (!prop)
290
return -ENOMEM;
291
292
of_add_property(np, prop);
293
294
return 0;
295
}
296
297
static int do_remove_property(char *buf, size_t bufsize)
298
{
299
struct device_node *np;
300
char *tmp;
301
buf = parse_node(buf, bufsize, &np);
302
303
if (!np)
304
return -ENODEV;
305
306
tmp = strchr(buf,' ');
307
if (tmp)
308
*tmp = '\0';
309
310
if (strlen(buf) == 0)
311
return -EINVAL;
312
313
return of_remove_property(np, of_find_property(np, buf, NULL));
314
}
315
316
static int do_update_property(char *buf, size_t bufsize)
317
{
318
struct device_node *np;
319
unsigned char *value;
320
char *name, *end, *next_prop;
321
int length;
322
struct property *newprop;
323
buf = parse_node(buf, bufsize, &np);
324
end = buf + bufsize;
325
326
if (!np)
327
return -ENODEV;
328
329
next_prop = parse_next_property(buf, end, &name, &length, &value);
330
if (!next_prop)
331
return -EINVAL;
332
333
if (!strlen(name))
334
return -ENODEV;
335
336
newprop = new_property(name, length, value, NULL);
337
if (!newprop)
338
return -ENOMEM;
339
340
if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
341
slb_set_size(*(int *)value);
342
343
return of_update_property(np, newprop);
344
}
345
346
/**
347
* ofdt_write - perform operations on the Open Firmware device tree
348
*
349
* @file: not used
350
* @buf: command and arguments
351
* @count: size of the command buffer
352
* @off: not used
353
*
354
* Operations supported at this time are addition and removal of
355
* whole nodes along with their properties. Operations on individual
356
* properties are not implemented (yet).
357
*/
358
static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
359
loff_t *off)
360
{
361
int rv;
362
char *kbuf;
363
char *tmp;
364
365
rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
366
if (rv)
367
return rv;
368
369
kbuf = memdup_user_nul(buf, count);
370
if (IS_ERR(kbuf))
371
return PTR_ERR(kbuf);
372
373
tmp = strchr(kbuf, ' ');
374
if (!tmp) {
375
rv = -EINVAL;
376
goto out;
377
}
378
*tmp = '\0';
379
tmp++;
380
381
if (!strcmp(kbuf, "add_node"))
382
rv = do_add_node(tmp, count - (tmp - kbuf));
383
else if (!strcmp(kbuf, "remove_node"))
384
rv = do_remove_node(tmp);
385
else if (!strcmp(kbuf, "add_property"))
386
rv = do_add_property(tmp, count - (tmp - kbuf));
387
else if (!strcmp(kbuf, "remove_property"))
388
rv = do_remove_property(tmp, count - (tmp - kbuf));
389
else if (!strcmp(kbuf, "update_property"))
390
rv = do_update_property(tmp, count - (tmp - kbuf));
391
else
392
rv = -EINVAL;
393
out:
394
kfree(kbuf);
395
return rv ? rv : count;
396
}
397
398
static const struct proc_ops ofdt_proc_ops = {
399
.proc_write = ofdt_write,
400
.proc_lseek = noop_llseek,
401
};
402
403
/* create /proc/powerpc/ofdt write-only by root */
404
static int proc_ppc64_create_ofdt(void)
405
{
406
struct proc_dir_entry *ent;
407
408
ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);
409
if (ent)
410
proc_set_size(ent, 0);
411
412
return 0;
413
}
414
machine_device_initcall(pseries, proc_ppc64_create_ofdt);
415
416