linux/fs/proc/proc_devtree.c
<<
>>
Prefs
   1/*
   2 * proc_devtree.c - handles /proc/device-tree
   3 *
   4 * Copyright 1997 Paul Mackerras
   5 */
   6#include <linux/errno.h>
   7#include <linux/init.h>
   8#include <linux/time.h>
   9#include <linux/proc_fs.h>
  10#include <linux/stat.h>
  11#include <linux/string.h>
  12#include <asm/prom.h>
  13#include <asm/uaccess.h>
  14#include "internal.h"
  15
  16#ifndef HAVE_ARCH_DEVTREE_FIXUPS
  17static inline void set_node_proc_entry(struct device_node *np,
  18                                       struct proc_dir_entry *de)
  19{
  20}
  21#endif
  22
  23static struct proc_dir_entry *proc_device_tree;
  24
  25/*
  26 * Supply data on a read from /proc/device-tree/node/property.
  27 */
  28static int property_read_proc(char *page, char **start, off_t off,
  29                              int count, int *eof, void *data)
  30{
  31        struct property *pp = data;
  32        int n;
  33
  34        if (off >= pp->length) {
  35                *eof = 1;
  36                return 0;
  37        }
  38        n = pp->length - off;
  39        if (n > count)
  40                n = count;
  41        else
  42                *eof = 1;
  43        memcpy(page, (char *)pp->value + off, n);
  44        *start = page;
  45        return n;
  46}
  47
  48/*
  49 * For a node with a name like "gc@10", we make symlinks called "gc"
  50 * and "@10" to it.
  51 */
  52
  53/*
  54 * Add a property to a node
  55 */
  56static struct proc_dir_entry *
  57__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp,
  58                const char *name)
  59{
  60        struct proc_dir_entry *ent;
  61
  62        /*
  63         * Unfortunately proc_register puts each new entry
  64         * at the beginning of the list.  So we rearrange them.
  65         */
  66        ent = create_proc_read_entry(name,
  67                                     strncmp(name, "security-", 9)
  68                                     ? S_IRUGO : S_IRUSR, de,
  69                                     property_read_proc, pp);
  70        if (ent == NULL)
  71                return NULL;
  72
  73        if (!strncmp(name, "security-", 9))
  74                ent->size = 0; /* don't leak number of password chars */
  75        else
  76                ent->size = pp->length;
  77
  78        return ent;
  79}
  80
  81
  82void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
  83{
  84        __proc_device_tree_add_prop(pde, prop, prop->name);
  85}
  86
  87void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
  88                                  struct property *prop)
  89{
  90        remove_proc_entry(prop->name, pde);
  91}
  92
  93void proc_device_tree_update_prop(struct proc_dir_entry *pde,
  94                                  struct property *newprop,
  95                                  struct property *oldprop)
  96{
  97        struct proc_dir_entry *ent;
  98
  99        for (ent = pde->subdir; ent != NULL; ent = ent->next)
 100                if (ent->data == oldprop)
 101                        break;
 102        if (ent == NULL) {
 103                printk(KERN_WARNING "device-tree: property \"%s\" "
 104                       " does not exist\n", oldprop->name);
 105        } else {
 106                ent->data = newprop;
 107                ent->size = newprop->length;
 108        }
 109}
 110
 111/*
 112 * Various dodgy firmware might give us nodes and/or properties with
 113 * conflicting names. That's generally ok, except for exporting via /proc,
 114 * so munge names here to ensure they're unique.
 115 */
 116
 117static int duplicate_name(struct proc_dir_entry *de, const char *name)
 118{
 119        struct proc_dir_entry *ent;
 120        int found = 0;
 121
 122        spin_lock(&proc_subdir_lock);
 123
 124        for (ent = de->subdir; ent != NULL; ent = ent->next) {
 125                if (strcmp(ent->name, name) == 0) {
 126                        found = 1;
 127                        break;
 128                }
 129        }
 130
 131        spin_unlock(&proc_subdir_lock);
 132
 133        return found;
 134}
 135
 136static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de,
 137                const char *name)
 138{
 139        char *fixed_name;
 140        int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */
 141        int i = 1, size;
 142
 143realloc:
 144        fixed_name = kmalloc(fixup_len, GFP_KERNEL);
 145        if (fixed_name == NULL) {
 146                printk(KERN_ERR "device-tree: Out of memory trying to fixup "
 147                                "name \"%s\"\n", name);
 148                return name;
 149        }
 150
 151retry:
 152        size = snprintf(fixed_name, fixup_len, "%s#%d", name, i);
 153        size++; /* account for NULL */
 154
 155        if (size > fixup_len) {
 156                /* We ran out of space, free and reallocate. */
 157                kfree(fixed_name);
 158                fixup_len = size;
 159                goto realloc;
 160        }
 161
 162        if (duplicate_name(de, fixed_name)) {
 163                /* Multiple duplicates. Retry with a different offset. */
 164                i++;
 165                goto retry;
 166        }
 167
 168        printk(KERN_WARNING "device-tree: Duplicate name in %s, "
 169                        "renamed to \"%s\"\n", np->full_name, fixed_name);
 170
 171        return fixed_name;
 172}
 173
 174/*
 175 * Process a node, adding entries for its children and its properties.
 176 */
 177void proc_device_tree_add_node(struct device_node *np,
 178                               struct proc_dir_entry *de)
 179{
 180        struct property *pp;
 181        struct proc_dir_entry *ent;
 182        struct device_node *child;
 183        const char *p;
 184
 185        set_node_proc_entry(np, de);
 186        for (child = NULL; (child = of_get_next_child(np, child));) {
 187                /* Use everything after the last slash, or the full name */
 188                p = strrchr(child->full_name, '/');
 189                if (!p)
 190                        p = child->full_name;
 191                else
 192                        ++p;
 193
 194                if (duplicate_name(de, p))
 195                        p = fixup_name(np, de, p);
 196
 197                ent = proc_mkdir(p, de);
 198                if (ent == NULL)
 199                        break;
 200                proc_device_tree_add_node(child, ent);
 201        }
 202        of_node_put(child);
 203
 204        for (pp = np->properties; pp != NULL; pp = pp->next) {
 205                p = pp->name;
 206
 207                if (duplicate_name(de, p))
 208                        p = fixup_name(np, de, p);
 209
 210                ent = __proc_device_tree_add_prop(de, pp, p);
 211                if (ent == NULL)
 212                        break;
 213        }
 214}
 215
 216/*
 217 * Called on initialization to set up the /proc/device-tree subtree
 218 */
 219void __init proc_device_tree_init(void)
 220{
 221        struct device_node *root;
 222
 223        proc_device_tree = proc_mkdir("device-tree", NULL);
 224        if (proc_device_tree == NULL)
 225                return;
 226        root = of_find_node_by_path("/");
 227        if (root == NULL) {
 228                printk(KERN_ERR "/proc/device-tree: can't find root\n");
 229                return;
 230        }
 231        proc_device_tree_add_node(root, proc_device_tree);
 232        of_node_put(root);
 233}
 234