diff -Nru a/arch/ppc64/kernel/proc_ppc64.c b/arch/ppc64/kernel/proc_ppc64.c --- a/arch/ppc64/kernel/proc_ppc64.c Wed Oct 8 18:27:36 2003 +++ b/arch/ppc64/kernel/proc_ppc64.c Wed Oct 8 18:27:36 2003 @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -37,11 +39,19 @@ #include #include #include +#include struct proc_ppc64_t proc_ppc64; void proc_ppc64_create_paca(int num); +static void proc_ppc64_create_ofdt(struct proc_dir_entry *parent); +static ssize_t write_ofdt (struct file *, const char *, size_t, loff_t *); +static ssize_t writev_ofdt (struct file *, const struct iovec *, + unsigned long, loff_t *); +static int build_prop_list(const struct iovec *iov, unsigned long nprops, + int *nbytes, struct property **result); + static loff_t page_map_seek( struct file *file, loff_t off, int whence); static ssize_t page_map_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos); static int page_map_mmap( struct file *file, struct vm_area_struct *vma ); @@ -93,6 +103,8 @@ /* Placeholder for rtas interfaces. */ proc_ppc64.rtas = proc_mkdir("rtas", proc_ppc64.root); + proc_ppc64_create_ofdt(proc_ppc64.root); + return 0; } @@ -171,6 +183,182 @@ remap_page_range( vma, vma->vm_start, __pa(dp->data), dp->size, vma->vm_page_prot ); return 0; +} + +static struct file_operations ofdt_fops = { + write: write_ofdt, /* remove device */ + writev: writev_ofdt /* add device */ +}; + +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; + } +} + +static ssize_t write_ofdt(struct file *file, const char __user *buf, size_t nbytes, loff_t *off) +{ + char *pathname = NULL; + int buflen; /* includes the null byte */ + ssize_t err; + + if (0 == (buflen = strlen_user(buf))) { + err = -EFAULT; + goto out; + } + + if (NULL == (pathname = kmalloc(nbytes + 1, GFP_KERNEL))) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(pathname, buf, nbytes)) { + err = -EFAULT; + goto out; + } + + pathname[nbytes] = '\0'; + err = of_remove_node(pathname); +out: + kfree(pathname); + return err ? err : nbytes; +} + +/* iov[0] should contain the full path of the new device (relative to + * /proc/device-tree), followed by alternating names and values. The + * value of iov[0] will be placed in the 'full_name' field of the new + * device_node. + */ +static ssize_t writev_ofdt(struct file *file, const struct iovec *iov, unsigned long niovs, loff_t *off) +{ + int nprops = (niovs - 1) / 2; + int err = 0; + int nbytes = 0; /* total number of bytes written */ + struct property *proplist = NULL; + char *full_name = NULL; /* new node's pathname relative to + * /proc/device-tree */ + + /* Need at least one property, and the value of niovs must be odd. */ + if (nprops < 1 || niovs > UIO_MAXIOV || niovs % 2 == 0) { + err = -EINVAL; + goto done; + } + + /* Process the path of the new device node. */ + if (NULL == (full_name = kmalloc(iov[0].iov_len, GFP_KERNEL))) { + err = -ENOMEM; + goto done; + } + + if (copy_from_user(full_name, iov[0].iov_base, iov[0].iov_len)) { + err = -EFAULT; + goto cleanup; + } + + full_name[iov[0].iov_len - 1] = '\0'; + + if ((err = build_prop_list(&iov[1], nprops, &nbytes, &proplist))) + goto cleanup; + + nbytes += iov[0].iov_len; + + if ((err = of_add_node(full_name, proplist))) + /* need to clean up proplist */ + goto cleanup; + +cleanup: + if (err) { + struct property *prop, *next; + + kfree(full_name); + for (prop = proplist; prop != NULL; prop = next) { + next = prop->next; + kfree(prop->name); + kfree(prop->value); + kfree(prop); + } + } +done: + return err ? err : nbytes; +} + +/* Utility function to build a property list from an iovec array. + * iov[0] : first property's name + * iov[1] : first property's value + * ... + * nprops : number of properties to place in the list (not # of iovecs!) + * nbytes : for recording total number of bytes read from iov + * result : for recording the location of the resulting list + */ +static int build_prop_list(const struct iovec *iov, unsigned long nprops, + int *nbytes, struct property **result) +{ + int err = 0; + struct property *proplist = NULL; + struct property *prop = NULL; + struct property *next = NULL; /* for error path */ + int i; + + *nbytes = 0; + + for (i = 0; i < nprops * 2; i++) { + void *from = iov[i].iov_base; + size_t len = iov[i].iov_len; + void *to = NULL; + + nbytes += len; + + if (i % 2 == 0) { + struct property *lastprop = prop; + prop = kmalloc(sizeof(struct property), GFP_KERNEL); + if (prop == NULL) { + err = -ENOMEM; + goto cleanup; + } + memset(prop, 0, sizeof(*prop)); + if (lastprop) + lastprop->next = prop; + else + proplist = prop; + } + + if (NULL == (to = kmalloc(len, GFP_KERNEL))) { + err = -ENOMEM; + goto cleanup; + } + + if (copy_from_user(to, from, len)) { + err = -EFAULT; + goto cleanup; + } + + if (i % 2 == 0) { + prop->name = (char *)to; + prop->name[len - 1] = '\0'; + } else { + prop->value = (unsigned char *)to; + prop->length = len; + } + } + *result = proplist; + return 0; + +cleanup: + for (prop = proplist; prop != NULL; prop = next) { + next = prop->next; + kfree(prop->name); + kfree(prop->value); + kfree(prop); + } + return err; } fs_initcall(proc_ppc64_init); diff -Nru a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c --- a/arch/ppc64/kernel/prom.c Wed Oct 8 18:27:36 2003 +++ b/arch/ppc64/kernel/prom.c Wed Oct 8 18:27:36 2003 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include "open_pic.h" @@ -149,6 +151,10 @@ int boot_cpuid = 0; struct device_node *allnodes = 0; +/* use when traversing tree through the allnext, child, sibling, + * or parent members of struct device_node. + */ +static rwlock_t devtree_lock = RW_LOCK_UNLOCKED; static unsigned long call_prom(const char *service, int nargs, int nret, ...); static void prom_panic(const char *reason); @@ -163,6 +169,11 @@ static struct bi_record * prom_bi_rec_verify(struct bi_record *); static unsigned long prom_bi_rec_reserve(unsigned long); static struct device_node *find_phandle(phandle); +static void of_node_cleanup(struct device_node *); +static struct device_node *derive_parent(const char *); +static void add_node_proc_entries(struct device_node *); +static void remove_node_proc_entries(struct device_node *); +static int of_finish_dynamic_node(struct device_node *); #ifdef DEBUG_PROM void prom_dump_lmb(void); @@ -1967,8 +1978,8 @@ /******* * * New implementation of the OF "find" APIs, return a refcounted - * object, call of_node_put() when done. Currently, still lacks - * locking as old implementation, this is being done for ppc64. + * object, call of_node_put() when done. The device tree and list + * are protected by a rw_lock. * * Note that property management will need some locking as well, * this isn't dealt with yet. @@ -1989,14 +2000,18 @@ struct device_node *of_find_node_by_name(struct device_node *from, const char *name) { - struct device_node *np = from ? from->allnext : allnodes; + struct device_node *np; + read_lock(&devtree_lock); + np = from ? from->allnext : allnodes; for (; np != 0; np = np->allnext) - if (np->name != 0 && strcasecmp(np->name, name) == 0) + if (np->name != 0 && strcasecmp(np->name, name) == 0 + && of_node_get(np)) break; if (from) of_node_put(from); - return of_node_get(np); + read_unlock(&devtree_lock); + return np; } /** @@ -2013,14 +2028,18 @@ struct device_node *of_find_node_by_type(struct device_node *from, const char *type) { - struct device_node *np = from ? from->allnext : allnodes; + struct device_node *np; + read_lock(&devtree_lock); + np = from ? from->allnext : allnodes; for (; np != 0; np = np->allnext) - if (np->type != 0 && strcasecmp(np->type, type) == 0) + if (np->type != 0 && strcasecmp(np->type, type) == 0 + && of_node_get(np)) break; if (from) of_node_put(from); - return of_node_get(np); + read_unlock(&devtree_lock); + return np; } /** @@ -2040,18 +2059,21 @@ struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible) { - struct device_node *np = from ? from->allnext : allnodes; + struct device_node *np; + read_lock(&devtree_lock); + np = from ? from->allnext : allnodes; for (; np != 0; np = np->allnext) { if (type != NULL && !(np->type != 0 && strcasecmp(np->type, type) == 0)) continue; - if (device_is_compatible(np, compatible)) + if (device_is_compatible(np, compatible) && of_node_get(np)) break; } if (from) of_node_put(from); - return of_node_get(np); + read_unlock(&devtree_lock); + return np; } /** @@ -2065,10 +2087,13 @@ { struct device_node *np = allnodes; + read_lock(&devtree_lock); for (; np != 0; np = np->allnext) - if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0 + && of_node_get(np)) break; - return of_node_get(np); + read_unlock(&devtree_lock); + return np; } /** @@ -2081,11 +2106,17 @@ */ struct device_node *of_find_all_nodes(struct device_node *prev) { - struct device_node *np = prev ? prev->allnext : allnodes; + struct device_node *np; + read_lock(&devtree_lock); + np = prev ? prev->allnext : allnodes; + for (; np != 0; np = np->allnext) + if (of_node_get(np)) + break; if (prev) of_node_put(prev); - return of_node_get(np); + read_unlock(&devtree_lock); + return np; } /** @@ -2097,7 +2128,15 @@ */ struct device_node *of_get_parent(const struct device_node *node) { - return node ? of_node_get(node->parent) : NULL; + struct device_node *np; + + if (!node) + return NULL; + + read_lock(&devtree_lock); + np = of_node_get(node->parent); + read_unlock(&devtree_lock); + return np; } /** @@ -2111,13 +2150,16 @@ struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev) { - struct device_node *next = prev ? prev->sibling : node->child; + struct device_node *next; + read_lock(&devtree_lock); + next = prev ? prev->sibling : node->child; for (; next != 0; next = next->sibling) if (of_node_get(next)) break; if (prev) of_node_put(prev); + read_unlock(&devtree_lock); return next; } @@ -2130,7 +2172,11 @@ */ struct device_node *of_node_get(struct device_node *node) { - return node; + if (node && !OF_IS_STALE(node)) { + atomic_inc(&node->_users); + return node; + } + return NULL; } /** @@ -2141,6 +2187,334 @@ */ void of_node_put(struct device_node *node) { + if (!node) + return; + + WARN_ON(0 == atomic_read(&node->_users)); + + if (OF_IS_STALE(node)) { + if (atomic_dec_and_test(&node->_users)) { + of_node_cleanup(node); + return; + } + } + else + atomic_dec(&node->_users); +} + +/** + * of_node_cleanup - release a dynamically allocated node + * @arg: Node to be released + */ +static void of_node_cleanup(struct device_node *node) +{ + struct property *prop = node->properties; + + if (!OF_IS_DYNAMIC(node)) + return; + while (prop) { + struct property *next = prop->next; + kfree(prop->name); + kfree(prop->value); + kfree(prop); + prop = next; + } + kfree(node->intrs); + kfree(node->addrs); + kfree(node->full_name); + kfree(node); +} + +/** + * derive_parent - basically like dirname(1) + * @path: the full_name of a node to be added to the tree + * + * Returns the node which should be the parent of the node + * described by path. E.g., for path = "/foo/bar", returns + * the node with full_name = "/foo". + */ +static struct device_node *derive_parent(const char *path) +{ + struct device_node *parent = NULL; + char *parent_path = "/"; + size_t parent_path_len = strrchr(path, '/') - path + 1; + + /* reject if path is "/" */ + if (!strcmp(path, "/")) + return NULL; + + if (strrchr(path, '/') != path) { + parent_path = kmalloc(parent_path_len, GFP_KERNEL); + if (!parent_path) + return NULL; + strlcpy(parent_path, path, parent_path_len); + } + parent = of_find_node_by_path(parent_path); + if (strcmp(parent_path, "/")) + kfree(parent_path); + return parent; +} + +/* + * Routines for "runtime" addition and removal of device tree nodes. + */ + +/* + * Given a path and a property list, construct an OF device node, add + * it to the device tree and global list, and place it in + * /proc/device-tree. This function may sleep. + */ +int of_add_node(const char *path, struct property *proplist) +{ + struct device_node *np; + int err = 0; + + np = kmalloc(sizeof(struct device_node), GFP_KERNEL); + if (!np) + return -ENOMEM; + + memset(np, 0, sizeof(*np)); + np->full_name = (char *)path; + np->properties = proplist; + OF_MARK_DYNAMIC(np); + of_node_get(np); + np->parent = derive_parent(path); + if (!np->parent) { + kfree(np); + return -EINVAL; /* could also be ENOMEM, though */ + } + + if (0 != (err = of_finish_dynamic_node(np))) { + kfree(np); + return err; + } + + write_lock(&devtree_lock); + np->sibling = np->parent->child; + np->allnext = allnodes; + np->parent->child = np; + allnodes = np; + write_unlock(&devtree_lock); + + add_node_proc_entries(np); + + of_node_put(np->parent); + of_node_put(np); + return 0; +} + +/* + * Remove an OF device node from the system. + */ +int of_remove_node(const char *path) +{ + struct device_node *np, *parent, *child; + + np = of_find_node_by_path(path); + if (np == NULL) + return -ENODEV; + + parent = of_get_parent(np); + child = of_get_next_child(np, NULL); + if (child && !child->child && !child->sibling) { + /* For now, we will allow removal of a + * node with one and only one child, so + * that we can support removing a slot with + * an IOA in it. More general support for + * subtree removal to be implemented later, if + * necessary. + */ + of_remove_node(child->full_name); + } + else if (child) { + of_node_put(np); + of_node_put(child); + of_node_put(parent); + return -EINVAL; + } + of_node_put(child); + + write_lock(&devtree_lock); + OF_MARK_STALE(np); + remove_node_proc_entries(np); + if (allnodes == np) + allnodes = np->allnext; + else { + struct device_node *prev; + for (prev = allnodes; + prev->allnext != np; + prev = prev->allnext) + ; + prev->allnext = np->allnext; + } + + if (np->parent->child == np) + np->parent->child = np->sibling; + else { + struct device_node *prevsib; + for (prevsib = np->parent->child; + prevsib->sibling != np; + prevsib = prevsib->sibling) + ; + prevsib->sibling = np->sibling; + } + write_unlock(&devtree_lock); + of_node_put(parent); + of_node_put(np); + return 0; +} + +/* + * Add a node to /proc/device-tree. + */ +static void add_node_proc_entries(struct device_node *np) +{ + struct proc_dir_entry *ent; + + ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde); + if (ent) + proc_device_tree_add_node(np, ent); +} + +static void remove_node_proc_entries(struct device_node *np) +{ + struct property *pp = np->properties; + struct device_node *parent = np->parent; + + while (pp) { + remove_proc_entry(pp->name, np->pde); + pp = pp->next; + } + + /* Assuming that symlinks have the same parent directory as + * np->pde. + */ + if (np->name_link) + remove_proc_entry(np->name_link->name, parent->pde); + if (np->addr_link) + remove_proc_entry(np->addr_link->name, parent->pde); + if (np->pde) + remove_proc_entry(np->pde->name, parent->pde); +} + +/* + * Fix up the uninitialized fields in a new device node: + * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields + * + * A lot of boot-time code is duplicated here, because functions such + * as finish_node_interrupts, interpret_pci_props, etc. cannot use the + * slab allocator. + * + * This should probably be split up into smaller chunks. + */ + +static int of_finish_dynamic_node(struct device_node *node) +{ + struct device_node *parent = of_get_parent(node); + u32 *regs; + unsigned int *ints; + int intlen, intrcells; + int i, j, n, err = 0; + unsigned int *irq; + struct device_node *ic; + + node->name = get_property(node, "name", 0); + node->type = get_property(node, "device_type", 0); + + if (!parent) { + err = -ENODEV; + goto out; + } + + /* do the work of interpret_pci_props */ + if (parent->type && !strcmp(parent->type, "pci")) { + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l; + + pci_addrs = (struct pci_reg_property *) + get_property(node, "assigned-addresses", &l); + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = kmalloc(sizeof(struct address_range) * + (l / sizeof(struct pci_reg_property)), + GFP_KERNEL); + if (!adr) { + err = -ENOMEM; + goto out; + } + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = pci_addrs[i].addr.a_lo; + adr[i].size = pci_addrs[i].size_lo; + ++i; + } + node->addrs = adr; + node->n_addrs = i; + } + } + + /* now do the work of finish_node_interrupts */ + + ints = (unsigned int *) get_property(node, "interrupts", &intlen); + if (!ints) + goto out; + + intrcells = prom_n_intr_cells(node); + intlen /= intrcells * sizeof(unsigned int); + node->n_intrs = intlen; + node->intrs = kmalloc(sizeof(struct interrupt_info) * intlen, + GFP_KERNEL); + if (!node->intrs) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < intlen; ++i) { + node->intrs[i].line = 0; + node->intrs[i].sense = 1; + n = map_interrupt(&irq, &ic, node, ints, intrcells); + if (n <= 0) + continue; + node->intrs[i].line = irq_offset_up(irq[0]); + if (n > 1) + node->intrs[i].sense = irq[1]; + if (n > 2) { + printk(KERN_DEBUG "hmmm, got %d intr cells for %s:", n, + node->full_name); + for (j = 0; j < n; ++j) + printk(" %d", irq[j]); + printk("\n"); + } + ints += intrcells; + } + + /* now do the rough equivalent of update_dn_pci_info, this + * probably is not correct for phb's, but should work for + * IOAs and slots. + */ + + node->phb = parent->phb; + + regs = (u32 *)get_property(node, "reg", 0); + if (regs) { + node->busno = (regs[0] >> 16) & 0xff; + node->devfn = (regs[0] >> 8) & 0xff; + } + + /* fixing up tce_table */ + + if(strcmp(node->name, "pci") == 0 && + get_property(node, "ibm,dma-window", NULL)) { + node->bussubno = node->busno; + create_pci_bus_tce_table((unsigned long)node); + } + else + node->tce_table = parent->tce_table; + +out: + of_node_put(parent); + return err; } /* diff -Nru a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c --- a/fs/proc/proc_devtree.c Wed Oct 8 18:27:36 2003 +++ b/fs/proc/proc_devtree.c Wed Oct 8 18:27:36 2003 @@ -11,6 +11,20 @@ #include #include +#ifndef HAVE_ARCH_DEVTREE_FIXUPS +static inline void set_node_proc_entry(struct device_node *np, struct proc_dir_entry *de) +{ +} + +static void inline set_node_name_link(struct device_node *np, struct proc_dir_entry *de) +{ +} + +static void inline set_node_addr_link(struct device_node *np, struct proc_dir_entry *de) +{ +} +#endif + static struct proc_dir_entry *proc_device_tree; /* @@ -44,7 +58,7 @@ /* * Process a node, adding entries for its children and its properties. */ -static void add_node(struct device_node *np, struct proc_dir_entry *de) +void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *de) { struct property *pp; struct proc_dir_entry *ent; @@ -53,6 +67,7 @@ int l; struct proc_dir_entry *list, **lastp, *al; + set_node_proc_entry(np, de); lastp = &list; for (pp = np->properties; pp != 0; pp = pp->next) { /* @@ -67,7 +82,8 @@ *lastp = ent; lastp = &ent->next; } - for (child = np->child; child != 0; child = child->sibling) { + child = NULL; + while ((child = of_get_next_child(np, child))) { p = strrchr(child->full_name, '/'); if (p == 0) p = child->full_name; @@ -82,7 +98,7 @@ break; *lastp = ent; lastp = &ent->next; - add_node(child, ent); + proc_device_tree_add_node(child, ent); /* * If we left the address part on the name, consider @@ -95,26 +111,32 @@ * If this is the first node with a given name property, * add a symlink with the name property as its name. */ - for (sib = np->child; sib != child; sib = sib->sibling) + sib = NULL; + while ((sib = of_get_next_child(np, sib)) && sib != child) if (sib->name && strcmp(sib->name, child->name) == 0) break; if (sib == child && strncmp(p, child->name, l) != 0) { al = proc_symlink(child->name, de, ent->name); - if (al == 0) + if (al == 0) { + of_node_put(sib); break; + } + set_node_name_link(child, al); *lastp = al; lastp = &al->next; } - + of_node_put(sib); /* * Add another directory with the @address part as its name. */ al = proc_symlink(at, de, ent->name); if (al == 0) break; + set_node_addr_link(child, al); *lastp = al; lastp = &al->next; } + of_node_put(child); *lastp = 0; de->subdir = list; } @@ -130,10 +152,11 @@ proc_device_tree = proc_mkdir("device-tree", 0); if (proc_device_tree == 0) return; - root = find_path_device("/"); + root = of_find_node_by_path("/"); if (root == 0) { printk(KERN_ERR "/proc/device-tree: can't find root\n"); return; } - add_node(root, proc_device_tree); + proc_device_tree_add_node(root, proc_device_tree); + of_node_put(root); } diff -Nru a/include/asm-ppc64/prom.h b/include/asm-ppc64/prom.h --- a/include/asm-ppc64/prom.h Wed Oct 8 18:27:36 2003 +++ b/include/asm-ppc64/prom.h Wed Oct 8 18:27:36 2003 @@ -14,6 +14,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include +#include #define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) #define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) @@ -143,7 +145,43 @@ struct device_node *sibling; struct device_node *next; /* next device of same type */ struct device_node *allnext; /* next in list of all nodes */ -}; + struct proc_dir_entry *pde; /* this node's proc directory */ + struct proc_dir_entry *name_link; /* name symlink */ + struct proc_dir_entry *addr_link; /* addr symlink */ + atomic_t _users; /* reference count */ + unsigned long _flags; +}; + +/* flag descriptions */ +#define OF_STALE 0 /* node is slated for deletion */ +#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ + +#define OF_IS_STALE(x) test_bit(OF_STALE, &x->_flags) +#define OF_MARK_STALE(x) set_bit(OF_STALE, &x->_flags) +#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) +#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) + +/* + * Until 32-bit ppc can add proc_dir_entries to its device_node + * definition, we cannot refer to pde, name_link, and addr_link + * in arch-independent code. + */ +#define HAVE_ARCH_DEVTREE_FIXUPS + +static inline void set_node_proc_entry(struct device_node *dn, struct proc_dir_entry *de) +{ + dn->pde = de; +} + +static void inline set_node_name_link(struct device_node *dn, struct proc_dir_entry *de) +{ + dn->name_link = de; +} + +static void inline set_node_addr_link(struct device_node *dn, struct proc_dir_entry *de) +{ + dn->addr_link = de; +} typedef u32 prom_arg_t; @@ -193,6 +231,10 @@ struct device_node *prev); extern struct device_node *of_node_get(struct device_node *node); extern void of_node_put(struct device_node *node); + +/* For updating the device tree at runtime */ +extern int of_add_node(const char *path, struct property *proplist); +extern int of_remove_node(const char *path); /* Other Prototypes */ extern void abort(void); diff -Nru a/include/linux/proc_fs.h b/include/linux/proc_fs.h --- a/include/linux/proc_fs.h Wed Oct 8 18:27:36 2003 +++ b/include/linux/proc_fs.h Wed Oct 8 18:27:36 2003 @@ -132,6 +132,7 @@ * proc_devtree.c */ extern void proc_device_tree_init(void); +extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *); /* * proc_rtas.c