1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
269a80d3fSPaul Mackerras /*
369a80d3fSPaul Mackerras  * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
469a80d3fSPaul Mackerras  * Hotplug and Dynamic Logical Partitioning on RPA platforms).
569a80d3fSPaul Mackerras  *
669a80d3fSPaul Mackerras  * Copyright (C) 2005 Nathan Lynch
769a80d3fSPaul Mackerras  * Copyright (C) 2005 IBM Corporation
869a80d3fSPaul Mackerras  */
969a80d3fSPaul Mackerras 
1069a80d3fSPaul Mackerras #include <linux/kernel.h>
1169a80d3fSPaul Mackerras #include <linux/notifier.h>
1269a80d3fSPaul Mackerras #include <linux/proc_fs.h>
13*99df7a28SNathan Lynch #include <linux/security.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
151cf3d8b3SNathan Fontenot #include <linux/of.h>
1669a80d3fSPaul Mackerras 
17e8222502SBenjamin Herrenschmidt #include <asm/machdep.h>
187c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
1946db2f86SBrian King #include <asm/mmu.h>
2069a80d3fSPaul Mackerras 
21948ad1acSAndy Shevchenko #include "of_helpers.h"
2269a80d3fSPaul Mackerras 
pSeries_reconfig_add_node(const char * path,struct property * proplist)2369a80d3fSPaul Mackerras static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
2469a80d3fSPaul Mackerras {
2569a80d3fSPaul Mackerras 	struct device_node *np;
2669a80d3fSPaul Mackerras 	int err = -ENOMEM;
2769a80d3fSPaul Mackerras 
2869a80d3fSPaul Mackerras 	np = kzalloc(sizeof(*np), GFP_KERNEL);
2969a80d3fSPaul Mackerras 	if (!np)
3069a80d3fSPaul Mackerras 		goto out_err;
3169a80d3fSPaul Mackerras 
3206665989SRob Herring 	np->full_name = kstrdup(kbasename(path), GFP_KERNEL);
3369a80d3fSPaul Mackerras 	if (!np->full_name)
3469a80d3fSPaul Mackerras 		goto out_err;
3569a80d3fSPaul Mackerras 
3669a80d3fSPaul Mackerras 	np->properties = proplist;
37d3b814bbSMichael Ellerman 	of_node_set_flag(np, OF_DYNAMIC);
3897a9a717STyrel Datwyler 	of_node_init(np);
3969a80d3fSPaul Mackerras 
40948ad1acSAndy Shevchenko 	np->parent = pseries_of_derive_parent(path);
4169a80d3fSPaul Mackerras 	if (IS_ERR(np->parent)) {
4269a80d3fSPaul Mackerras 		err = PTR_ERR(np->parent);
4369a80d3fSPaul Mackerras 		goto out_err;
4469a80d3fSPaul Mackerras 	}
4569a80d3fSPaul Mackerras 
461cf3d8b3SNathan Fontenot 	err = of_attach_node(np);
473aef19f0SAkinobu Mita 	if (err) {
4869a80d3fSPaul Mackerras 		printk(KERN_ERR "Failed to add device node %s\n", path);
4969a80d3fSPaul Mackerras 		goto out_err;
5069a80d3fSPaul Mackerras 	}
5169a80d3fSPaul Mackerras 
5269a80d3fSPaul Mackerras 	of_node_put(np->parent);
5369a80d3fSPaul Mackerras 
5469a80d3fSPaul Mackerras 	return 0;
5569a80d3fSPaul Mackerras 
5669a80d3fSPaul Mackerras out_err:
5769a80d3fSPaul Mackerras 	if (np) {
5869a80d3fSPaul Mackerras 		of_node_put(np->parent);
5969a80d3fSPaul Mackerras 		kfree(np->full_name);
6069a80d3fSPaul Mackerras 		kfree(np);
6169a80d3fSPaul Mackerras 	}
6269a80d3fSPaul Mackerras 	return err;
6369a80d3fSPaul Mackerras }
6469a80d3fSPaul Mackerras 
pSeries_reconfig_remove_node(struct device_node * np)6569a80d3fSPaul Mackerras static int pSeries_reconfig_remove_node(struct device_node *np)
6669a80d3fSPaul Mackerras {
6769a80d3fSPaul Mackerras 	struct device_node *parent, *child;
6869a80d3fSPaul Mackerras 
6969a80d3fSPaul Mackerras 	parent = of_get_parent(np);
7069a80d3fSPaul Mackerras 	if (!parent)
7169a80d3fSPaul Mackerras 		return -EINVAL;
7269a80d3fSPaul Mackerras 
7369a80d3fSPaul Mackerras 	if ((child = of_get_next_child(np, NULL))) {
7469a80d3fSPaul Mackerras 		of_node_put(child);
75842decbdSJulia Lawall 		of_node_put(parent);
7669a80d3fSPaul Mackerras 		return -EBUSY;
7769a80d3fSPaul Mackerras 	}
7869a80d3fSPaul Mackerras 
7969a80d3fSPaul Mackerras 	of_detach_node(np);
8069a80d3fSPaul Mackerras 	of_node_put(parent);
8169a80d3fSPaul Mackerras 	return 0;
8269a80d3fSPaul Mackerras }
8369a80d3fSPaul Mackerras 
8469a80d3fSPaul Mackerras /*
85188917e1SBenjamin Herrenschmidt  * /proc/powerpc/ofdt - yucky binary interface for adding and removing
8669a80d3fSPaul Mackerras  * OF device nodes.  Should be deprecated as soon as we get an
8769a80d3fSPaul Mackerras  * in-kernel wrapper for the RTAS ibm,configure-connector call.
8869a80d3fSPaul Mackerras  */
8969a80d3fSPaul Mackerras 
release_prop_list(const struct property * prop)9069a80d3fSPaul Mackerras static void release_prop_list(const struct property *prop)
9169a80d3fSPaul Mackerras {
9269a80d3fSPaul Mackerras 	struct property *next;
9369a80d3fSPaul Mackerras 	for (; prop; prop = next) {
9469a80d3fSPaul Mackerras 		next = prop->next;
9569a80d3fSPaul Mackerras 		kfree(prop->name);
9669a80d3fSPaul Mackerras 		kfree(prop->value);
9769a80d3fSPaul Mackerras 		kfree(prop);
9869a80d3fSPaul Mackerras 	}
9969a80d3fSPaul Mackerras 
10069a80d3fSPaul Mackerras }
10169a80d3fSPaul Mackerras 
10269a80d3fSPaul Mackerras /**
10369a80d3fSPaul Mackerras  * parse_next_property - process the next property from raw input buffer
10469a80d3fSPaul Mackerras  * @buf: input buffer, must be nul-terminated
10569a80d3fSPaul Mackerras  * @end: end of the input buffer + 1, for validation
10669a80d3fSPaul Mackerras  * @name: return value; set to property name in buf
10769a80d3fSPaul Mackerras  * @length: return value; set to length of value
10869a80d3fSPaul Mackerras  * @value: return value; set to the property value in buf
10969a80d3fSPaul Mackerras  *
11069a80d3fSPaul Mackerras  * Note that the caller must make copies of the name and value returned,
11169a80d3fSPaul Mackerras  * this function does no allocation or copying of the data.  Return value
11269a80d3fSPaul Mackerras  * is set to the next name in buf, or NULL on error.
11369a80d3fSPaul Mackerras  */
parse_next_property(char * buf,char * end,char ** name,int * length,unsigned char ** value)11469a80d3fSPaul Mackerras static char * parse_next_property(char *buf, char *end, char **name, int *length,
11569a80d3fSPaul Mackerras 				  unsigned char **value)
11669a80d3fSPaul Mackerras {
11769a80d3fSPaul Mackerras 	char *tmp;
11869a80d3fSPaul Mackerras 
11969a80d3fSPaul Mackerras 	*name = buf;
12069a80d3fSPaul Mackerras 
12169a80d3fSPaul Mackerras 	tmp = strchr(buf, ' ');
12269a80d3fSPaul Mackerras 	if (!tmp) {
12369a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
124e48b1b45SHarvey Harrison 		       __func__, __LINE__);
12569a80d3fSPaul Mackerras 		return NULL;
12669a80d3fSPaul Mackerras 	}
12769a80d3fSPaul Mackerras 	*tmp = '\0';
12869a80d3fSPaul Mackerras 
12969a80d3fSPaul Mackerras 	if (++tmp >= end) {
13069a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
131e48b1b45SHarvey Harrison 		       __func__, __LINE__);
13269a80d3fSPaul Mackerras 		return NULL;
13369a80d3fSPaul Mackerras 	}
13469a80d3fSPaul Mackerras 
13569a80d3fSPaul Mackerras 	/* now we're on the length */
13669a80d3fSPaul Mackerras 	*length = -1;
13769a80d3fSPaul Mackerras 	*length = simple_strtoul(tmp, &tmp, 10);
13869a80d3fSPaul Mackerras 	if (*length == -1) {
13969a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
140e48b1b45SHarvey Harrison 		       __func__, __LINE__);
14169a80d3fSPaul Mackerras 		return NULL;
14269a80d3fSPaul Mackerras 	}
14369a80d3fSPaul Mackerras 	if (*tmp != ' ' || ++tmp >= end) {
14469a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
145e48b1b45SHarvey Harrison 		       __func__, __LINE__);
14669a80d3fSPaul Mackerras 		return NULL;
14769a80d3fSPaul Mackerras 	}
14869a80d3fSPaul Mackerras 
14969a80d3fSPaul Mackerras 	/* now we're on the value */
15069a80d3fSPaul Mackerras 	*value = tmp;
15169a80d3fSPaul Mackerras 	tmp += *length;
15269a80d3fSPaul Mackerras 	if (tmp > end) {
15369a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
154e48b1b45SHarvey Harrison 		       __func__, __LINE__);
15569a80d3fSPaul Mackerras 		return NULL;
15669a80d3fSPaul Mackerras 	}
15769a80d3fSPaul Mackerras 	else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
15869a80d3fSPaul Mackerras 		printk(KERN_ERR "property parse failed in %s at line %d\n",
159e48b1b45SHarvey Harrison 		       __func__, __LINE__);
16069a80d3fSPaul Mackerras 		return NULL;
16169a80d3fSPaul Mackerras 	}
16269a80d3fSPaul Mackerras 	tmp++;
16369a80d3fSPaul Mackerras 
16469a80d3fSPaul Mackerras 	/* and now we should be on the next name, or the end */
16569a80d3fSPaul Mackerras 	return tmp;
16669a80d3fSPaul Mackerras }
16769a80d3fSPaul Mackerras 
new_property(const char * name,const int length,const unsigned char * value,struct property * last)16869a80d3fSPaul Mackerras static struct property *new_property(const char *name, const int length,
16969a80d3fSPaul Mackerras 				     const unsigned char *value, struct property *last)
17069a80d3fSPaul Mackerras {
171f8485350SYan Burman 	struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
17269a80d3fSPaul Mackerras 
17369a80d3fSPaul Mackerras 	if (!new)
17469a80d3fSPaul Mackerras 		return NULL;
17569a80d3fSPaul Mackerras 
176c22618a1SGrant Likely 	if (!(new->name = kstrdup(name, GFP_KERNEL)))
17769a80d3fSPaul Mackerras 		goto cleanup;
17869a80d3fSPaul Mackerras 	if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
17969a80d3fSPaul Mackerras 		goto cleanup;
18069a80d3fSPaul Mackerras 
18169a80d3fSPaul Mackerras 	memcpy(new->value, value, length);
18269a80d3fSPaul Mackerras 	*(((char *)new->value) + length) = 0;
18369a80d3fSPaul Mackerras 	new->length = length;
18469a80d3fSPaul Mackerras 	new->next = last;
18569a80d3fSPaul Mackerras 	return new;
18669a80d3fSPaul Mackerras 
18769a80d3fSPaul Mackerras cleanup:
18869a80d3fSPaul Mackerras 	kfree(new->name);
18969a80d3fSPaul Mackerras 	kfree(new->value);
19069a80d3fSPaul Mackerras 	kfree(new);
19169a80d3fSPaul Mackerras 	return NULL;
19269a80d3fSPaul Mackerras }
19369a80d3fSPaul Mackerras 
do_add_node(char * buf,size_t bufsize)19469a80d3fSPaul Mackerras static int do_add_node(char *buf, size_t bufsize)
19569a80d3fSPaul Mackerras {
19669a80d3fSPaul Mackerras 	char *path, *end, *name;
19769a80d3fSPaul Mackerras 	struct device_node *np;
19869a80d3fSPaul Mackerras 	struct property *prop = NULL;
19969a80d3fSPaul Mackerras 	unsigned char* value;
20069a80d3fSPaul Mackerras 	int length, rv = 0;
20169a80d3fSPaul Mackerras 
20269a80d3fSPaul Mackerras 	end = buf + bufsize;
20369a80d3fSPaul Mackerras 	path = buf;
20469a80d3fSPaul Mackerras 	buf = strchr(buf, ' ');
20569a80d3fSPaul Mackerras 	if (!buf)
20669a80d3fSPaul Mackerras 		return -EINVAL;
20769a80d3fSPaul Mackerras 	*buf = '\0';
20869a80d3fSPaul Mackerras 	buf++;
20969a80d3fSPaul Mackerras 
21069a80d3fSPaul Mackerras 	if ((np = of_find_node_by_path(path))) {
21169a80d3fSPaul Mackerras 		of_node_put(np);
21269a80d3fSPaul Mackerras 		return -EINVAL;
21369a80d3fSPaul Mackerras 	}
21469a80d3fSPaul Mackerras 
21569a80d3fSPaul Mackerras 	/* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
21669a80d3fSPaul Mackerras 	while (buf < end &&
21769a80d3fSPaul Mackerras 	       (buf = parse_next_property(buf, end, &name, &length, &value))) {
21869a80d3fSPaul Mackerras 		struct property *last = prop;
21969a80d3fSPaul Mackerras 
22069a80d3fSPaul Mackerras 		prop = new_property(name, length, value, last);
22169a80d3fSPaul Mackerras 		if (!prop) {
22269a80d3fSPaul Mackerras 			rv = -ENOMEM;
22369a80d3fSPaul Mackerras 			prop = last;
22469a80d3fSPaul Mackerras 			goto out;
22569a80d3fSPaul Mackerras 		}
22669a80d3fSPaul Mackerras 	}
22769a80d3fSPaul Mackerras 	if (!buf) {
22869a80d3fSPaul Mackerras 		rv = -EINVAL;
22969a80d3fSPaul Mackerras 		goto out;
23069a80d3fSPaul Mackerras 	}
23169a80d3fSPaul Mackerras 
23269a80d3fSPaul Mackerras 	rv = pSeries_reconfig_add_node(path, prop);
23369a80d3fSPaul Mackerras 
23469a80d3fSPaul Mackerras out:
23569a80d3fSPaul Mackerras 	if (rv)
23669a80d3fSPaul Mackerras 		release_prop_list(prop);
23769a80d3fSPaul Mackerras 	return rv;
23869a80d3fSPaul Mackerras }
23969a80d3fSPaul Mackerras 
do_remove_node(char * buf)24069a80d3fSPaul Mackerras static int do_remove_node(char *buf)
24169a80d3fSPaul Mackerras {
24269a80d3fSPaul Mackerras 	struct device_node *node;
24369a80d3fSPaul Mackerras 	int rv = -ENODEV;
24469a80d3fSPaul Mackerras 
24569a80d3fSPaul Mackerras 	if ((node = of_find_node_by_path(buf)))
24669a80d3fSPaul Mackerras 		rv = pSeries_reconfig_remove_node(node);
24769a80d3fSPaul Mackerras 
24869a80d3fSPaul Mackerras 	of_node_put(node);
24969a80d3fSPaul Mackerras 	return rv;
25069a80d3fSPaul Mackerras }
25169a80d3fSPaul Mackerras 
parse_node(char * buf,size_t bufsize,struct device_node ** npp)252610d9151SDave C Boutcher static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
253610d9151SDave C Boutcher {
254610d9151SDave C Boutcher 	char *handle_str;
255610d9151SDave C Boutcher 	phandle handle;
256610d9151SDave C Boutcher 	*npp = NULL;
257610d9151SDave C Boutcher 
258610d9151SDave C Boutcher 	handle_str = buf;
259610d9151SDave C Boutcher 
260610d9151SDave C Boutcher 	buf = strchr(buf, ' ');
261610d9151SDave C Boutcher 	if (!buf)
262610d9151SDave C Boutcher 		return NULL;
263610d9151SDave C Boutcher 	*buf = '\0';
264610d9151SDave C Boutcher 	buf++;
265610d9151SDave C Boutcher 
2664b6e805eSNathan Fontenot 	handle = simple_strtoul(handle_str, NULL, 0);
267610d9151SDave C Boutcher 
268610d9151SDave C Boutcher 	*npp = of_find_node_by_phandle(handle);
269610d9151SDave C Boutcher 	return buf;
270610d9151SDave C Boutcher }
271610d9151SDave C Boutcher 
do_add_property(char * buf,size_t bufsize)272610d9151SDave C Boutcher static int do_add_property(char *buf, size_t bufsize)
273610d9151SDave C Boutcher {
274610d9151SDave C Boutcher 	struct property *prop = NULL;
275610d9151SDave C Boutcher 	struct device_node *np;
276610d9151SDave C Boutcher 	unsigned char *value;
277610d9151SDave C Boutcher 	char *name, *end;
278610d9151SDave C Boutcher 	int length;
279610d9151SDave C Boutcher 	end = buf + bufsize;
280610d9151SDave C Boutcher 	buf = parse_node(buf, bufsize, &np);
281610d9151SDave C Boutcher 
282610d9151SDave C Boutcher 	if (!np)
283610d9151SDave C Boutcher 		return -ENODEV;
284610d9151SDave C Boutcher 
285610d9151SDave C Boutcher 	if (parse_next_property(buf, end, &name, &length, &value) == NULL)
286610d9151SDave C Boutcher 		return -EINVAL;
287610d9151SDave C Boutcher 
288610d9151SDave C Boutcher 	prop = new_property(name, length, value, NULL);
289610d9151SDave C Boutcher 	if (!prop)
290610d9151SDave C Boutcher 		return -ENOMEM;
291610d9151SDave C Boutcher 
29279d1c712SNathan Fontenot 	of_add_property(np, prop);
293610d9151SDave C Boutcher 
294610d9151SDave C Boutcher 	return 0;
295610d9151SDave C Boutcher }
296610d9151SDave C Boutcher 
do_remove_property(char * buf,size_t bufsize)297610d9151SDave C Boutcher static int do_remove_property(char *buf, size_t bufsize)
298610d9151SDave C Boutcher {
299610d9151SDave C Boutcher 	struct device_node *np;
300610d9151SDave C Boutcher 	char *tmp;
301610d9151SDave C Boutcher 	buf = parse_node(buf, bufsize, &np);
302610d9151SDave C Boutcher 
303610d9151SDave C Boutcher 	if (!np)
304610d9151SDave C Boutcher 		return -ENODEV;
305610d9151SDave C Boutcher 
306610d9151SDave C Boutcher 	tmp = strchr(buf,' ');
307610d9151SDave C Boutcher 	if (tmp)
308610d9151SDave C Boutcher 		*tmp = '\0';
309610d9151SDave C Boutcher 
310610d9151SDave C Boutcher 	if (strlen(buf) == 0)
311610d9151SDave C Boutcher 		return -EINVAL;
312610d9151SDave C Boutcher 
313925e2d1dSSuraj Jitindar Singh 	return of_remove_property(np, of_find_property(np, buf, NULL));
314610d9151SDave C Boutcher }
315610d9151SDave C Boutcher 
do_update_property(char * buf,size_t bufsize)316610d9151SDave C Boutcher static int do_update_property(char *buf, size_t bufsize)
317610d9151SDave C Boutcher {
318610d9151SDave C Boutcher 	struct device_node *np;
319610d9151SDave C Boutcher 	unsigned char *value;
3203c3f67eaSNathan Fontenot 	char *name, *end, *next_prop;
3211cf3d8b3SNathan Fontenot 	int length;
322475d0094SDong Aisheng 	struct property *newprop;
323610d9151SDave C Boutcher 	buf = parse_node(buf, bufsize, &np);
324610d9151SDave C Boutcher 	end = buf + bufsize;
325610d9151SDave C Boutcher 
326610d9151SDave C Boutcher 	if (!np)
327610d9151SDave C Boutcher 		return -ENODEV;
328610d9151SDave C Boutcher 
3293c3f67eaSNathan Fontenot 	next_prop = parse_next_property(buf, end, &name, &length, &value);
3303c3f67eaSNathan Fontenot 	if (!next_prop)
331610d9151SDave C Boutcher 		return -EINVAL;
332610d9151SDave C Boutcher 
333475d0094SDong Aisheng 	if (!strlen(name))
334475d0094SDong Aisheng 		return -ENODEV;
335475d0094SDong Aisheng 
336610d9151SDave C Boutcher 	newprop = new_property(name, length, value, NULL);
337610d9151SDave C Boutcher 	if (!newprop)
338610d9151SDave C Boutcher 		return -ENOMEM;
339610d9151SDave C Boutcher 
34046db2f86SBrian King 	if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
34146db2f86SBrian King 		slb_set_size(*(int *)value);
34246db2f86SBrian King 
34379d1c712SNathan Fontenot 	return of_update_property(np, newprop);
344610d9151SDave C Boutcher }
345610d9151SDave C Boutcher 
34669a80d3fSPaul Mackerras /**
34769a80d3fSPaul Mackerras  * ofdt_write - perform operations on the Open Firmware device tree
34869a80d3fSPaul Mackerras  *
34969a80d3fSPaul Mackerras  * @file: not used
35069a80d3fSPaul Mackerras  * @buf: command and arguments
35169a80d3fSPaul Mackerras  * @count: size of the command buffer
35269a80d3fSPaul Mackerras  * @off: not used
35369a80d3fSPaul Mackerras  *
35469a80d3fSPaul Mackerras  * Operations supported at this time are addition and removal of
35569a80d3fSPaul Mackerras  * whole nodes along with their properties.  Operations on individual
35669a80d3fSPaul Mackerras  * properties are not implemented (yet).
35769a80d3fSPaul Mackerras  */
ofdt_write(struct file * file,const char __user * buf,size_t count,loff_t * off)35869a80d3fSPaul Mackerras static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
35969a80d3fSPaul Mackerras 			  loff_t *off)
36069a80d3fSPaul Mackerras {
36131f8eb75SMichael Ellerman 	int rv;
36269a80d3fSPaul Mackerras 	char *kbuf;
36369a80d3fSPaul Mackerras 	char *tmp;
36469a80d3fSPaul Mackerras 
365*99df7a28SNathan Lynch 	rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
366*99df7a28SNathan Lynch 	if (rv)
367*99df7a28SNathan Lynch 		return rv;
368*99df7a28SNathan Lynch 
36937832251SGeliang Tang 	kbuf = memdup_user_nul(buf, count);
37037832251SGeliang Tang 	if (IS_ERR(kbuf))
37137832251SGeliang Tang 		return PTR_ERR(kbuf);
37269a80d3fSPaul Mackerras 
37369a80d3fSPaul Mackerras 	tmp = strchr(kbuf, ' ');
37469a80d3fSPaul Mackerras 	if (!tmp) {
37569a80d3fSPaul Mackerras 		rv = -EINVAL;
37669a80d3fSPaul Mackerras 		goto out;
37769a80d3fSPaul Mackerras 	}
37869a80d3fSPaul Mackerras 	*tmp = '\0';
37969a80d3fSPaul Mackerras 	tmp++;
38069a80d3fSPaul Mackerras 
38169a80d3fSPaul Mackerras 	if (!strcmp(kbuf, "add_node"))
38269a80d3fSPaul Mackerras 		rv = do_add_node(tmp, count - (tmp - kbuf));
38369a80d3fSPaul Mackerras 	else if (!strcmp(kbuf, "remove_node"))
38469a80d3fSPaul Mackerras 		rv = do_remove_node(tmp);
385610d9151SDave C Boutcher 	else if (!strcmp(kbuf, "add_property"))
386610d9151SDave C Boutcher 		rv = do_add_property(tmp, count - (tmp - kbuf));
387610d9151SDave C Boutcher 	else if (!strcmp(kbuf, "remove_property"))
388610d9151SDave C Boutcher 		rv = do_remove_property(tmp, count - (tmp - kbuf));
389610d9151SDave C Boutcher 	else if (!strcmp(kbuf, "update_property"))
390610d9151SDave C Boutcher 		rv = do_update_property(tmp, count - (tmp - kbuf));
39169a80d3fSPaul Mackerras 	else
39269a80d3fSPaul Mackerras 		rv = -EINVAL;
39369a80d3fSPaul Mackerras out:
39469a80d3fSPaul Mackerras 	kfree(kbuf);
39569a80d3fSPaul Mackerras 	return rv ? rv : count;
39669a80d3fSPaul Mackerras }
39769a80d3fSPaul Mackerras 
39897a32539SAlexey Dobriyan static const struct proc_ops ofdt_proc_ops = {
39997a32539SAlexey Dobriyan 	.proc_write	= ofdt_write,
40097a32539SAlexey Dobriyan 	.proc_lseek	= noop_llseek,
40169a80d3fSPaul Mackerras };
40269a80d3fSPaul Mackerras 
403188917e1SBenjamin Herrenschmidt /* create /proc/powerpc/ofdt write-only by root */
proc_ppc64_create_ofdt(void)40469a80d3fSPaul Mackerras static int proc_ppc64_create_ofdt(void)
40569a80d3fSPaul Mackerras {
40669a80d3fSPaul Mackerras 	struct proc_dir_entry *ent;
40769a80d3fSPaul Mackerras 
40897a32539SAlexey Dobriyan 	ent = proc_create("powerpc/ofdt", 0200, NULL, &ofdt_proc_ops);
40966747138SDenis V. Lunev 	if (ent)
410271a15eaSDavid Howells 		proc_set_size(ent, 0);
41169a80d3fSPaul Mackerras 
41269a80d3fSPaul Mackerras 	return 0;
41369a80d3fSPaul Mackerras }
4148e83e905SMichael Ellerman machine_device_initcall(pseries, proc_ppc64_create_ofdt);
415