vserver 1.9.5.x5
[linux-2.6.git] / arch / ppc64 / kernel / proc_ppc64.c
1 /*
2  * arch/ppc64/kernel/proc_ppc64.c
3  *
4  * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19  */
20
21 #include <linux/config.h>
22 #include <linux/init.h>
23 #include <linux/mm.h>
24 #include <linux/proc_fs.h>
25 #include <linux/slab.h>
26 #include <linux/kernel.h>
27
28 #include <asm/systemcfg.h>
29 #include <asm/rtas.h>
30 #include <asm/uaccess.h>
31 #include <asm/prom.h>
32
33 static loff_t  page_map_seek( struct file *file, loff_t off, int whence);
34 static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes,
35                               loff_t *ppos);
36 static int     page_map_mmap( struct file *file, struct vm_area_struct *vma );
37
38 static struct file_operations page_map_fops = {
39         .llseek = page_map_seek,
40         .read   = page_map_read,
41         .mmap   = page_map_mmap
42 };
43
44 #ifdef CONFIG_PPC_PSERIES
45 /* routines for /proc/ppc64/ofdt */
46 static ssize_t ofdt_write(struct file *, const char __user *, size_t, loff_t *);
47 static void proc_ppc64_create_ofdt(void);
48 static int do_remove_node(char *);
49 static int do_add_node(char *, size_t);
50 static void release_prop_list(const struct property *);
51 static struct property *new_property(const char *, const int, const unsigned char *, struct property *);
52 static char * parse_next_property(char *, char *, char **, int *, unsigned char**);
53 static struct file_operations ofdt_fops = {
54         .write = ofdt_write
55 };
56 #endif
57
58 /*
59  * Create the ppc64 and ppc64/rtas directories early. This allows us to
60  * assume that they have been previously created in drivers.
61  */
62 static int __init proc_ppc64_create(void)
63 {
64         struct proc_dir_entry *root;
65
66         root = proc_mkdir("ppc64", NULL);
67         if (!root)
68                 return 1;
69
70         if (!(systemcfg->platform & PLATFORM_PSERIES))
71                 return 0;
72
73         if (!proc_mkdir("rtas", root))
74                 return 1;
75
76         if (!proc_symlink("rtas", NULL, "ppc64/rtas"))
77                 return 1;
78
79         return 0;
80 }
81 core_initcall(proc_ppc64_create);
82
83 static int __init proc_ppc64_init(void)
84 {
85         struct proc_dir_entry *pde;
86
87         pde = create_proc_entry("ppc64/systemcfg", S_IFREG|S_IRUGO, NULL);
88         if (!pde)
89                 return 1;
90         pde->nlink = 1;
91         pde->data = systemcfg;
92         pde->size = 4096;
93         pde->proc_fops = &page_map_fops;
94
95 #ifdef CONFIG_PPC_PSERIES
96         if ((systemcfg->platform & PLATFORM_PSERIES))
97                 proc_ppc64_create_ofdt();
98 #endif
99
100         return 0;
101 }
102 __initcall(proc_ppc64_init);
103
104 static loff_t page_map_seek( struct file *file, loff_t off, int whence)
105 {
106         loff_t new;
107         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
108
109         switch(whence) {
110         case 0:
111                 new = off;
112                 break;
113         case 1:
114                 new = file->f_pos + off;
115                 break;
116         case 2:
117                 new = dp->size + off;
118                 break;
119         default:
120                 return -EINVAL;
121         }
122         if ( new < 0 || new > dp->size )
123                 return -EINVAL;
124         return (file->f_pos = new);
125 }
126
127 static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes,
128                               loff_t *ppos)
129 {
130         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
131         return simple_read_from_buffer(buf, nbytes, ppos, dp->data, dp->size);
132 }
133
134 static int page_map_mmap( struct file *file, struct vm_area_struct *vma )
135 {
136         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
137
138         vma->vm_flags |= VM_SHM | VM_LOCKED;
139
140         if ((vma->vm_end - vma->vm_start) > dp->size)
141                 return -EINVAL;
142
143         remap_pfn_range(vma, vma->vm_start, __pa(dp->data) >> PAGE_SHIFT,
144                                                 dp->size, vma->vm_page_prot);
145         return 0;
146 }
147
148 #ifdef CONFIG_PPC_PSERIES
149 /* create /proc/ppc64/ofdt write-only by root */
150 static void proc_ppc64_create_ofdt(void)
151 {
152         struct proc_dir_entry *ent;
153
154         ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL);
155         if (ent) {
156                 ent->nlink = 1;
157                 ent->data = NULL;
158                 ent->size = 0;
159                 ent->proc_fops = &ofdt_fops;
160         }
161 }
162
163 /**
164  * ofdt_write - perform operations on the Open Firmware device tree
165  *
166  * @file: not used
167  * @buf: command and arguments
168  * @count: size of the command buffer
169  * @off: not used
170  *
171  * Operations supported at this time are addition and removal of
172  * whole nodes along with their properties.  Operations on individual
173  * properties are not implemented (yet).
174  */
175 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
176                           loff_t *off)
177 {
178         int rv = 0;
179         char *kbuf;
180         char *tmp;
181
182         if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) {
183                 rv = -ENOMEM;
184                 goto out;
185         }
186         if (copy_from_user(kbuf, buf, count)) {
187                 rv = -EFAULT;
188                 goto out;
189         }
190
191         kbuf[count] = '\0';
192
193         tmp = strchr(kbuf, ' ');
194         if (!tmp) {
195                 rv = -EINVAL;
196                 goto out;
197         }
198         *tmp = '\0';
199         tmp++;
200
201         if (!strcmp(kbuf, "add_node"))
202                 rv = do_add_node(tmp, count - (tmp - kbuf));
203         else if (!strcmp(kbuf, "remove_node"))
204                 rv = do_remove_node(tmp);
205         else
206                 rv = -EINVAL;
207 out:
208         kfree(kbuf);
209         return rv ? rv : count;
210 }
211
212 static int do_remove_node(char *buf)
213 {
214         struct device_node *node;
215         int rv = -ENODEV;
216
217         if ((node = of_find_node_by_path(buf)))
218                 rv = of_remove_node(node);
219
220         of_node_put(node);
221         return rv;
222 }
223
224 static int do_add_node(char *buf, size_t bufsize)
225 {
226         char *path, *end, *name;
227         struct device_node *np;
228         struct property *prop = NULL;
229         unsigned char* value;
230         int length, rv = 0;
231
232         end = buf + bufsize;
233         path = buf;
234         buf = strchr(buf, ' ');
235         if (!buf)
236                 return -EINVAL;
237         *buf = '\0';
238         buf++;
239
240         if ((np = of_find_node_by_path(path))) {
241                 of_node_put(np);
242                 return -EINVAL;
243         }
244
245         /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
246         while (buf < end &&
247                (buf = parse_next_property(buf, end, &name, &length, &value))) {
248                 struct property *last = prop;
249
250                 prop = new_property(name, length, value, last);
251                 if (!prop) {
252                         rv = -ENOMEM;
253                         prop = last;
254                         goto out;
255                 }
256         }
257         if (!buf) {
258                 rv = -EINVAL;
259                 goto out;
260         }
261
262         rv = of_add_node(path, prop);
263
264 out:
265         if (rv)
266                 release_prop_list(prop);
267         return rv;
268 }
269
270 static struct property *new_property(const char *name, const int length,
271                                      const unsigned char *value, struct property *last)
272 {
273         struct property *new = kmalloc(sizeof(*new), GFP_KERNEL);
274
275         if (!new)
276                 return NULL;
277         memset(new, 0, sizeof(*new));
278
279         if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL)))
280                 goto cleanup;
281         if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
282                 goto cleanup;
283
284         strcpy(new->name, name);
285         memcpy(new->value, value, length);
286         *(((char *)new->value) + length) = 0;
287         new->length = length;
288         new->next = last;
289         return new;
290
291 cleanup:
292         if (new->name)
293                 kfree(new->name);
294         if (new->value)
295                 kfree(new->value);
296         kfree(new);
297         return NULL;
298 }
299
300 /**
301  * parse_next_property - process the next property from raw input buffer
302  * @buf: input buffer, must be nul-terminated
303  * @end: end of the input buffer + 1, for validation
304  * @name: return value; set to property name in buf
305  * @length: return value; set to length of value
306  * @value: return value; set to the property value in buf
307  *
308  * Note that the caller must make copies of the name and value returned,
309  * this function does no allocation or copying of the data.  Return value
310  * is set to the next name in buf, or NULL on error.
311  */
312 static char * parse_next_property(char *buf, char *end, char **name, int *length,
313                                   unsigned char **value)
314 {
315         char *tmp;
316
317         *name = buf;
318
319         tmp = strchr(buf, ' ');
320         if (!tmp) {
321                 printk(KERN_ERR "property parse failed in %s at line %d\n",
322                        __FUNCTION__, __LINE__);
323                 return NULL;
324         }
325         *tmp = '\0';
326
327         if (++tmp >= end) {
328                 printk(KERN_ERR "property parse failed in %s at line %d\n",
329                        __FUNCTION__, __LINE__);
330                 return NULL;
331         }
332
333         /* now we're on the length */
334         *length = -1;
335         *length = simple_strtoul(tmp, &tmp, 10);
336         if (*length == -1) {
337                 printk(KERN_ERR "property parse failed in %s at line %d\n", 
338                        __FUNCTION__, __LINE__);
339                 return NULL;
340         }
341         if (*tmp != ' ' || ++tmp >= end) {
342                 printk(KERN_ERR "property parse failed in %s at line %d\n",
343                        __FUNCTION__, __LINE__);
344                 return NULL;
345         }
346
347         /* now we're on the value */
348         *value = tmp;
349         tmp += *length;
350         if (tmp > end) {
351                 printk(KERN_ERR "property parse failed in %s at line %d\n",
352                        __FUNCTION__, __LINE__);
353                 return NULL;
354         }
355         else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
356                 printk(KERN_ERR "property parse failed in %s at line %d\n",
357                        __FUNCTION__, __LINE__);
358                 return NULL;
359         }
360         tmp++;
361
362         /* and now we should be on the next name, or the end */
363         return tmp;
364 }
365
366 static void release_prop_list(const struct property *prop)
367 {
368         struct property *next;
369         for (; prop; prop = next) {
370                 next = prop->next;
371                 kfree(prop->name);
372                 kfree(prop->value);
373                 kfree(prop);
374         }
375
376 }
377 #endif  /* defined(CONFIG_PPC_PSERIES) */