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