ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[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/naca.h>
29 #include <asm/paca.h>
30 #include <asm/systemcfg.h>
31 #include <asm/rtas.h>
32 #include <asm/uaccess.h>
33 #include <asm/prom.h>
34
35 static loff_t  page_map_seek( struct file *file, loff_t off, int whence);
36 static ssize_t page_map_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos);
37 static int     page_map_mmap( struct file *file, struct vm_area_struct *vma );
38
39 static struct file_operations page_map_fops = {
40         .llseek = page_map_seek,
41         .read   = page_map_read,
42         .mmap   = page_map_mmap
43 };
44
45 #ifdef CONFIG_PPC_PSERIES
46 /* routines for /proc/ppc64/ofdt */
47 static ssize_t ofdt_write(struct file *, const char __user *, size_t, loff_t *);
48 static void proc_ppc64_create_ofdt(void);
49 static int do_remove_node(char *);
50 static int do_add_node(char *, size_t);
51 static void release_prop_list(const struct property *);
52 static struct property *new_property(const char *, const int, const unsigned char *, struct property *);
53 static char * parse_next_property(char *, char *, char **, int *, unsigned char**);
54 static struct file_operations ofdt_fops = {
55         .write = ofdt_write
56 };
57 #endif
58
59 /*
60  * NOTE: since paca data is always in flux the values will never be a
61  * consistant set.
62  */
63 static void __init proc_create_paca(struct proc_dir_entry *dir, int num)
64 {
65         struct proc_dir_entry *ent;
66         struct paca_struct *lpaca = paca + num;
67         char buf[16];
68
69         sprintf(buf, "%02x", num);
70         ent = create_proc_entry(buf, S_IRUSR, dir);
71         if (ent) {
72                 ent->nlink = 1;
73                 ent->data = lpaca;
74                 ent->size = 4096;
75                 ent->proc_fops = &page_map_fops;
76         }
77 }
78
79 /*
80  * Create the ppc64 and ppc64/rtas directories early. This allows us to
81  * assume that they have been previously created in drivers.
82  */
83 static int __init proc_ppc64_create(void)
84 {
85         struct proc_dir_entry *root;
86
87         root = proc_mkdir("ppc64", 0);
88         if (!root)
89                 return 1;
90
91         if (!(systemcfg->platform & PLATFORM_PSERIES))
92                 return 0;
93
94         if (!proc_mkdir("rtas", root))
95                 return 1;
96
97         if (!proc_symlink("rtas", 0, "ppc64/rtas"))
98                 return 1;
99
100         return 0;
101 }
102 core_initcall(proc_ppc64_create);
103
104 static int __init proc_ppc64_init(void)
105 {
106         unsigned long i;
107         struct proc_dir_entry *pde;
108
109         pde = create_proc_entry("ppc64/naca", S_IRUSR, NULL);
110         if (!pde)
111                 return 1;
112         pde->nlink = 1;
113         pde->data = naca;
114         pde->size = 4096;
115         pde->proc_fops = &page_map_fops;
116
117         pde = create_proc_entry("ppc64/systemcfg", S_IFREG|S_IRUGO, NULL);
118         if (!pde)
119                 return 1;
120         pde->nlink = 1;
121         pde->data = systemcfg;
122         pde->size = 4096;
123         pde->proc_fops = &page_map_fops;
124
125         /* /proc/ppc64/paca/XX -- raw paca contents.  Only readable to root */
126         pde = proc_mkdir("ppc64/paca", NULL);
127         if (!pde)
128                 return 1;
129         for_each_cpu(i)
130                 proc_create_paca(pde, i);
131
132 #ifdef CONFIG_PPC_PSERIES
133         if ((systemcfg->platform & PLATFORM_PSERIES))
134                 proc_ppc64_create_ofdt();
135 #endif
136
137         return 0;
138 }
139 __initcall(proc_ppc64_init);
140
141 static loff_t page_map_seek( struct file *file, loff_t off, int whence)
142 {
143         loff_t new;
144         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
145
146         switch(whence) {
147         case 0:
148                 new = off;
149                 break;
150         case 1:
151                 new = file->f_pos + off;
152                 break;
153         case 2:
154                 new = dp->size + off;
155                 break;
156         default:
157                 return -EINVAL;
158         }
159         if ( new < 0 || new > dp->size )
160                 return -EINVAL;
161         return (file->f_pos = new);
162 }
163
164 static ssize_t page_map_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos)
165 {
166         unsigned pos = *ppos;
167         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
168
169         if ( pos >= dp->size )
170                 return 0;
171         if ( nbytes >= dp->size )
172                 nbytes = dp->size;
173         if ( pos + nbytes > dp->size )
174                 nbytes = dp->size - pos;
175
176         copy_to_user( buf, (char *)dp->data + pos, nbytes );
177         *ppos = pos + nbytes;
178         return nbytes;
179 }
180
181 static int page_map_mmap( struct file *file, struct vm_area_struct *vma )
182 {
183         struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
184
185         vma->vm_flags |= VM_SHM | VM_LOCKED;
186
187         if ((vma->vm_end - vma->vm_start) > dp->size)
188                 return -EINVAL;
189
190         remap_page_range( vma, vma->vm_start, __pa(dp->data), dp->size, vma->vm_page_prot );
191         return 0;
192 }
193
194 #ifdef CONFIG_PPC_PSERIES
195 /* create /proc/ppc64/ofdt write-only by root */
196 static void proc_ppc64_create_ofdt(void)
197 {
198         struct proc_dir_entry *ent;
199
200         ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL);
201         if (ent) {
202                 ent->nlink = 1;
203                 ent->data = NULL;
204                 ent->size = 0;
205                 ent->proc_fops = &ofdt_fops;
206         }
207 }
208
209 /**
210  * ofdt_write - perform operations on the Open Firmware device tree
211  *
212  * @file: not used
213  * @buf: command and arguments
214  * @count: size of the command buffer
215  * @off: not used
216  *
217  * Operations supported at this time are addition and removal of
218  * whole nodes along with their properties.  Operations on individual
219  * properties are not implemented (yet).
220  */
221 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
222 {
223         int rv = 0;
224         char *kbuf;
225         char *tmp;
226
227         if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) {
228                 rv = -ENOMEM;
229                 goto out;
230         }
231         if (copy_from_user(kbuf, buf, count)) {
232                 rv = -EFAULT;
233                 goto out;
234         }
235
236         kbuf[count] = '\0';
237
238         tmp = strchr(kbuf, ' ');
239         if (!tmp) {
240                 rv = -EINVAL;
241                 goto out;
242         }
243         *tmp = '\0';
244         tmp++;
245
246         if (!strcmp(kbuf, "add_node"))
247                 rv = do_add_node(tmp, count - (tmp - kbuf));
248         else if (!strcmp(kbuf, "remove_node"))
249                 rv = do_remove_node(tmp);
250         else
251                 rv = -EINVAL;
252 out:
253         kfree(kbuf);
254         return rv ? rv : count;
255 }
256
257 static int do_remove_node(char *buf)
258 {
259         struct device_node *node;
260         int rv = -ENODEV;
261
262         if ((node = of_find_node_by_path(buf)))
263                 rv = of_remove_node(node);
264
265         of_node_put(node);
266         return rv;
267 }
268
269 static int do_add_node(char *buf, size_t bufsize)
270 {
271         char *path, *end, *name;
272         struct device_node *np;
273         struct property *prop = NULL;
274         unsigned char* value;
275         int length, rv = 0;
276
277         end = buf + bufsize;
278         path = buf;
279         buf = strchr(buf, ' ');
280         if (!buf)
281                 return -EINVAL;
282         *buf = '\0';
283         buf++;
284
285         if ((np = of_find_node_by_path(path))) {
286                 of_node_put(np);
287                 return -EINVAL;
288         }
289
290         /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
291         while (buf < end &&
292                (buf = parse_next_property(buf, end, &name, &length, &value))) {
293                 struct property *last = prop;
294
295                 prop = new_property(name, length, value, last);
296                 if (!prop) {
297                         rv = -ENOMEM;
298                         prop = last;
299                         goto out;
300                 }
301         }
302         if (!buf) {
303                 rv = -EINVAL;
304                 goto out;
305         }
306
307         rv = of_add_node(path, prop);
308
309 out:
310         if (rv)
311                 release_prop_list(prop);
312         return rv;
313 }
314
315 static struct property *new_property(const char *name, const int length, const unsigned char *value, struct property *last)
316 {
317         struct property *new = kmalloc(sizeof(*new), GFP_KERNEL);
318
319         if (!new)
320                 return NULL;
321         memset(new, 0, sizeof(*new));
322
323         if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL)))
324                 goto cleanup;
325         if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
326                 goto cleanup;
327
328         strcpy(new->name, name);
329         memcpy(new->value, value, length);
330         *(((char *)new->value) + length) = 0;
331         new->length = length;
332         new->next = last;
333         return new;
334
335 cleanup:
336         if (new->name)
337                 kfree(new->name);
338         if (new->value)
339                 kfree(new->value);
340         kfree(new);
341         return NULL;
342 }
343
344 /**
345  * parse_next_property - process the next property from raw input buffer
346  * @buf: input buffer, must be nul-terminated
347  * @end: end of the input buffer + 1, for validation
348  * @name: return value; set to property name in buf
349  * @length: return value; set to length of value
350  * @value: return value; set to the property value in buf
351  *
352  * Note that the caller must make copies of the name and value returned,
353  * this function does no allocation or copying of the data.  Return value
354  * is set to the next name in buf, or NULL on error.
355  */
356 static char * parse_next_property(char *buf, char *end, char **name, int *length, unsigned char **value)
357 {
358         char *tmp;
359
360         *name = buf;
361
362         tmp = strchr(buf, ' ');
363         if (!tmp) {
364                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
365                 return NULL;
366         }
367         *tmp = '\0';
368
369         if (++tmp >= end) {
370                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
371                 return NULL;
372         }
373
374         /* now we're on the length */
375         *length = -1;
376         *length = simple_strtoul(tmp, &tmp, 10);
377         if (*length == -1) {
378                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
379                 return NULL;
380         }
381         if (*tmp != ' ' || ++tmp >= end) {
382                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
383                 return NULL;
384         }
385
386         /* now we're on the value */
387         *value = tmp;
388         tmp += *length;
389         if (tmp > end) {
390                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
391                 return NULL;
392         }
393         else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
394                 printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__);
395                 return NULL;
396         }
397         tmp++;
398
399         /* and now we should be on the next name, or the end */
400         return tmp;
401 }
402
403 static void release_prop_list(const struct property *prop)
404 {
405         struct property *next;
406         for (; prop; prop = next) {
407                 next = prop->next;
408                 kfree(prop->name);
409                 kfree(prop->value);
410                 kfree(prop);
411         }
412
413 }
414 #endif  /* defined(CONFIG_PPC_PSERIES) */