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