[ppc64] Open Firmware device tree manipulation support, from Nathan Lynch Implementation of /proc/ppc64/ofdt, for manipulation of Open Firmware device tree (/proc/device-tree). Supports addition and removal of OF device nodes. --- arch/ppc64/kernel/proc_ppc64.c | 237 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 237 insertions(+) diff -puN arch/ppc64/kernel/proc_ppc64.c~ppc64-device_tree_updates_3 arch/ppc64/kernel/proc_ppc64.c --- 25/arch/ppc64/kernel/proc_ppc64.c~ppc64-device_tree_updates_3 2004-01-13 23:22:03.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/proc_ppc64.c 2004-01-13 23:22:03.000000000 -0800 @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include struct proc_ppc64_t proc_ppc64; @@ -52,6 +55,17 @@ static struct file_operations page_map_f .mmap = page_map_mmap }; +/* routines for /proc/ppc64/ofdt */ +static ssize_t ofdt_write(struct file *, const char __user *, size_t, loff_t *); +static void proc_ppc64_create_ofdt(struct proc_dir_entry *); +static int do_remove_node(char *); +static int do_add_node(char *, size_t); +static void release_prop_list(const struct property *); +static struct property *new_property(const char *, const int, const unsigned char *, struct property *); +static char * parse_next_property(char *, char *, char **, int *, unsigned char**); +static struct file_operations ofdt_fops = { + .write = ofdt_write +}; static int __init proc_ppc64_init(void) { @@ -93,6 +107,8 @@ static int __init proc_ppc64_init(void) /* Placeholder for rtas interfaces. */ proc_ppc64.rtas = proc_mkdir("rtas", proc_ppc64.root); + proc_ppc64_create_ofdt(proc_ppc64.root); + return 0; } @@ -173,5 +189,226 @@ static int page_map_mmap( struct file *f return 0; } +/* create /proc/ppc64/ofdt write-only by root */ +static void proc_ppc64_create_ofdt(struct proc_dir_entry *parent) +{ + struct proc_dir_entry *ent; + + ent = create_proc_entry("ofdt", S_IWUSR, parent); + if (ent) { + ent->nlink = 1; + ent->data = NULL; + ent->size = 0; + ent->proc_fops = &ofdt_fops; + } +} + +/** + * ofdt_write - perform operations on the Open Firmware device tree + * + * @file: not used + * @buf: command and arguments + * @count: size of the command buffer + * @off: not used + * + * Operations supported at this time are addition and removal of + * whole nodes along with their properties. Operations on individual + * properties are not implemented (yet). + */ +static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count, loff_t *off) +{ + int rv = 0; + char *kbuf; + char *tmp; + + if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) { + rv = -ENOMEM; + goto out; + } + if (copy_from_user(kbuf, buf, count)) { + rv = -EFAULT; + goto out; + } + + kbuf[count] = '\0'; + + tmp = strchr(kbuf, ' '); + if (!tmp) { + rv = -EINVAL; + goto out; + } + *tmp = '\0'; + tmp++; + + if (!strcmp(kbuf, "add_node")) + rv = do_add_node(tmp, 1 + count - (tmp - kbuf)); + else if (!strcmp(kbuf, "remove_node")) + rv = do_remove_node(tmp); + else + rv = -EINVAL; +out: + kfree(kbuf); + return rv ? rv : count; +} + +static int do_remove_node(char *buf) +{ + struct device_node *node; + int rv = 0; + + if ((node = of_find_node_by_path(buf))) + of_remove_node(node); + else + rv = -ENODEV; + + of_node_put(node); + return rv; +} + +static int do_add_node(char *buf, size_t bufsize) +{ + char *path, *end, *name; + struct device_node *np; + struct property *prop = NULL; + unsigned char* value; + int length, rv = 0; + + end = buf + bufsize; + path = buf; + buf = strchr(buf, ' '); + if (!buf) + return -EINVAL; + *buf = '\0'; + buf++; + + if ((np = of_find_node_by_path(path))) { + of_node_put(np); + return -EINVAL; + } + + /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */ + while (buf < end && + (buf = parse_next_property(buf, end, &name, &length, &value))) { + struct property *last = prop; + + prop = new_property(name, length, value, last); + if (!prop) { + rv = -ENOMEM; + prop = last; + goto out; + } + } + if (!buf) { + rv = -EINVAL; + goto out; + } + + rv = of_add_node(path, prop); + +out: + if (rv) + release_prop_list(prop); + return rv; +} + +static struct property *new_property(const char *name, const int length, const unsigned char *value, struct property *last) +{ + struct property *new = kmalloc(sizeof(*new), GFP_KERNEL); + + if (!new) + return NULL; + memset(new, 0, sizeof(*new)); + + if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL))) + goto cleanup; + if (!(new->value = kmalloc(length, GFP_KERNEL))) + goto cleanup; + + strcpy(new->name, name); + memcpy(new->value, value, length); + new->length = length; + new->next = last; + return new; + +cleanup: + if (new->name) + kfree(new->name); + if (new->value) + kfree(new->value); + kfree(new); + return NULL; +} + +/** + * parse_next_property - process the next property from raw input buffer + * @buf: input buffer, must be nul-terminated + * @end: end of the input buffer + 1, for validation + * @name: return value; set to property name in buf + * @length: return value; set to length of value + * @value: return value; set to the property value in buf + * + * Note that the caller must make copies of the name and value returned, + * this function does no allocation or copying of the data. Return value + * is set to the next name in buf, or NULL on error. + */ +static char * parse_next_property(char *buf, char *end, char **name, int *length, unsigned char **value) +{ + char *tmp; + + *name = buf; + + tmp = strchr(buf, ' '); + if (!tmp) { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + *tmp = '\0'; + + if (++tmp >= end) { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + + /* now we're on the length */ + *length = -1; + *length = simple_strtoul(tmp, &tmp, 10); + if (*length == -1) { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + if (*tmp != ' ' || ++tmp >= end) { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + + /* now we're on the value */ + *value = tmp; + tmp += *length; + if (tmp > end) { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + else if (tmp < end && *tmp != ' ' && *tmp != '\0') { + printk(KERN_ERR "property parse failed in %s at line %d\n", __FUNCTION__, __LINE__); + return NULL; + } + tmp++; + + /* and now we should be on the next name, or the end */ + return tmp; +} + +static void release_prop_list(const struct property *prop) +{ + struct property *next; + for (; prop; prop = next) { + next = prop->next; + kfree(prop->name); + kfree(prop->value); + kfree(prop); + } + +} + fs_initcall(proc_ppc64_init); _