1 /* 2 * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI 3 * Hotplug and Dynamic Logical Partitioning on RPA platforms). 4 * 5 * Copyright (C) 2005 Nathan Lynch 6 * Copyright (C) 2005 IBM Corporation 7 * 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License version 11 * 2 as published by the Free Software Foundation. 12 */ 13 14 #include <linux/kernel.h> 15 #include <linux/kref.h> 16 #include <linux/notifier.h> 17 #include <linux/proc_fs.h> 18 19 #include <asm/prom.h> 20 #include <asm/pSeries_reconfig.h> 21 #include <asm/uaccess.h> 22 23 24 25 /* 26 * Routines for "runtime" addition and removal of device tree nodes. 27 */ 28 #ifdef CONFIG_PROC_DEVICETREE 29 /* 30 * Add a node to /proc/device-tree. 31 */ 32 static void add_node_proc_entries(struct device_node *np) 33 { 34 struct proc_dir_entry *ent; 35 36 ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde); 37 if (ent) 38 proc_device_tree_add_node(np, ent); 39 } 40 41 static void remove_node_proc_entries(struct device_node *np) 42 { 43 struct property *pp = np->properties; 44 struct device_node *parent = np->parent; 45 46 while (pp) { 47 remove_proc_entry(pp->name, np->pde); 48 pp = pp->next; 49 } 50 if (np->pde) 51 remove_proc_entry(np->pde->name, parent->pde); 52 } 53 #else /* !CONFIG_PROC_DEVICETREE */ 54 static void add_node_proc_entries(struct device_node *np) 55 { 56 return; 57 } 58 59 static void remove_node_proc_entries(struct device_node *np) 60 { 61 return; 62 } 63 #endif /* CONFIG_PROC_DEVICETREE */ 64 65 /** 66 * derive_parent - basically like dirname(1) 67 * @path: the full_name of a node to be added to the tree 68 * 69 * Returns the node which should be the parent of the node 70 * described by path. E.g., for path = "/foo/bar", returns 71 * the node with full_name = "/foo". 72 */ 73 static struct device_node *derive_parent(const char *path) 74 { 75 struct device_node *parent = NULL; 76 char *parent_path = "/"; 77 size_t parent_path_len = strrchr(path, '/') - path + 1; 78 79 /* reject if path is "/" */ 80 if (!strcmp(path, "/")) 81 return ERR_PTR(-EINVAL); 82 83 if (strrchr(path, '/') != path) { 84 parent_path = kmalloc(parent_path_len, GFP_KERNEL); 85 if (!parent_path) 86 return ERR_PTR(-ENOMEM); 87 strlcpy(parent_path, path, parent_path_len); 88 } 89 parent = of_find_node_by_path(parent_path); 90 if (!parent) 91 return ERR_PTR(-EINVAL); 92 if (strcmp(parent_path, "/")) 93 kfree(parent_path); 94 return parent; 95 } 96 97 static struct notifier_block *pSeries_reconfig_chain; 98 99 int pSeries_reconfig_notifier_register(struct notifier_block *nb) 100 { 101 return notifier_chain_register(&pSeries_reconfig_chain, nb); 102 } 103 104 void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) 105 { 106 notifier_chain_unregister(&pSeries_reconfig_chain, nb); 107 } 108 109 static int pSeries_reconfig_add_node(const char *path, struct property *proplist) 110 { 111 struct device_node *np; 112 int err = -ENOMEM; 113 114 np = kzalloc(sizeof(*np), GFP_KERNEL); 115 if (!np) 116 goto out_err; 117 118 np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL); 119 if (!np->full_name) 120 goto out_err; 121 122 strcpy(np->full_name, path); 123 124 np->properties = proplist; 125 OF_MARK_DYNAMIC(np); 126 kref_init(&np->kref); 127 128 np->parent = derive_parent(path); 129 if (IS_ERR(np->parent)) { 130 err = PTR_ERR(np->parent); 131 goto out_err; 132 } 133 134 err = notifier_call_chain(&pSeries_reconfig_chain, 135 PSERIES_RECONFIG_ADD, np); 136 if (err == NOTIFY_BAD) { 137 printk(KERN_ERR "Failed to add device node %s\n", path); 138 err = -ENOMEM; /* For now, safe to assume kmalloc failure */ 139 goto out_err; 140 } 141 142 of_attach_node(np); 143 144 add_node_proc_entries(np); 145 146 of_node_put(np->parent); 147 148 return 0; 149 150 out_err: 151 if (np) { 152 of_node_put(np->parent); 153 kfree(np->full_name); 154 kfree(np); 155 } 156 return err; 157 } 158 159 static int pSeries_reconfig_remove_node(struct device_node *np) 160 { 161 struct device_node *parent, *child; 162 163 parent = of_get_parent(np); 164 if (!parent) 165 return -EINVAL; 166 167 if ((child = of_get_next_child(np, NULL))) { 168 of_node_put(child); 169 return -EBUSY; 170 } 171 172 remove_node_proc_entries(np); 173 174 notifier_call_chain(&pSeries_reconfig_chain, 175 PSERIES_RECONFIG_REMOVE, np); 176 of_detach_node(np); 177 178 of_node_put(parent); 179 of_node_put(np); /* Must decrement the refcount */ 180 return 0; 181 } 182 183 /* 184 * /proc/ppc64/ofdt - yucky binary interface for adding and removing 185 * OF device nodes. Should be deprecated as soon as we get an 186 * in-kernel wrapper for the RTAS ibm,configure-connector call. 187 */ 188 189 static void release_prop_list(const struct property *prop) 190 { 191 struct property *next; 192 for (; prop; prop = next) { 193 next = prop->next; 194 kfree(prop->name); 195 kfree(prop->value); 196 kfree(prop); 197 } 198 199 } 200 201 /** 202 * parse_next_property - process the next property from raw input buffer 203 * @buf: input buffer, must be nul-terminated 204 * @end: end of the input buffer + 1, for validation 205 * @name: return value; set to property name in buf 206 * @length: return value; set to length of value 207 * @value: return value; set to the property value in buf 208 * 209 * Note that the caller must make copies of the name and value returned, 210 * this function does no allocation or copying of the data. Return value 211 * is set to the next name in buf, or NULL on error. 212 */ 213 static char * parse_next_property(char *buf, char *end, char **name, int *length, 214 unsigned char **value) 215 { 216 char *tmp; 217 218 *name = buf; 219 220 tmp = strchr(buf, ' '); 221 if (!tmp) { 222 printk(KERN_ERR "property parse failed in %s at line %d\n", 223 __FUNCTION__, __LINE__); 224 return NULL; 225 } 226 *tmp = '\0'; 227 228 if (++tmp >= end) { 229 printk(KERN_ERR "property parse failed in %s at line %d\n", 230 __FUNCTION__, __LINE__); 231 return NULL; 232 } 233 234 /* now we're on the length */ 235 *length = -1; 236 *length = simple_strtoul(tmp, &tmp, 10); 237 if (*length == -1) { 238 printk(KERN_ERR "property parse failed in %s at line %d\n", 239 __FUNCTION__, __LINE__); 240 return NULL; 241 } 242 if (*tmp != ' ' || ++tmp >= end) { 243 printk(KERN_ERR "property parse failed in %s at line %d\n", 244 __FUNCTION__, __LINE__); 245 return NULL; 246 } 247 248 /* now we're on the value */ 249 *value = tmp; 250 tmp += *length; 251 if (tmp > end) { 252 printk(KERN_ERR "property parse failed in %s at line %d\n", 253 __FUNCTION__, __LINE__); 254 return NULL; 255 } 256 else if (tmp < end && *tmp != ' ' && *tmp != '\0') { 257 printk(KERN_ERR "property parse failed in %s at line %d\n", 258 __FUNCTION__, __LINE__); 259 return NULL; 260 } 261 tmp++; 262 263 /* and now we should be on the next name, or the end */ 264 return tmp; 265 } 266 267 static struct property *new_property(const char *name, const int length, 268 const unsigned char *value, struct property *last) 269 { 270 struct property *new = kmalloc(sizeof(*new), GFP_KERNEL); 271 272 if (!new) 273 return NULL; 274 memset(new, 0, sizeof(*new)); 275 276 if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL))) 277 goto cleanup; 278 if (!(new->value = kmalloc(length + 1, GFP_KERNEL))) 279 goto cleanup; 280 281 strcpy(new->name, name); 282 memcpy(new->value, value, length); 283 *(((char *)new->value) + length) = 0; 284 new->length = length; 285 new->next = last; 286 return new; 287 288 cleanup: 289 kfree(new->name); 290 kfree(new->value); 291 kfree(new); 292 return NULL; 293 } 294 295 static int do_add_node(char *buf, size_t bufsize) 296 { 297 char *path, *end, *name; 298 struct device_node *np; 299 struct property *prop = NULL; 300 unsigned char* value; 301 int length, rv = 0; 302 303 end = buf + bufsize; 304 path = buf; 305 buf = strchr(buf, ' '); 306 if (!buf) 307 return -EINVAL; 308 *buf = '\0'; 309 buf++; 310 311 if ((np = of_find_node_by_path(path))) { 312 of_node_put(np); 313 return -EINVAL; 314 } 315 316 /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */ 317 while (buf < end && 318 (buf = parse_next_property(buf, end, &name, &length, &value))) { 319 struct property *last = prop; 320 321 prop = new_property(name, length, value, last); 322 if (!prop) { 323 rv = -ENOMEM; 324 prop = last; 325 goto out; 326 } 327 } 328 if (!buf) { 329 rv = -EINVAL; 330 goto out; 331 } 332 333 rv = pSeries_reconfig_add_node(path, prop); 334 335 out: 336 if (rv) 337 release_prop_list(prop); 338 return rv; 339 } 340 341 static int do_remove_node(char *buf) 342 { 343 struct device_node *node; 344 int rv = -ENODEV; 345 346 if ((node = of_find_node_by_path(buf))) 347 rv = pSeries_reconfig_remove_node(node); 348 349 of_node_put(node); 350 return rv; 351 } 352 353 /** 354 * ofdt_write - perform operations on the Open Firmware device tree 355 * 356 * @file: not used 357 * @buf: command and arguments 358 * @count: size of the command buffer 359 * @off: not used 360 * 361 * Operations supported at this time are addition and removal of 362 * whole nodes along with their properties. Operations on individual 363 * properties are not implemented (yet). 364 */ 365 static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count, 366 loff_t *off) 367 { 368 int rv = 0; 369 char *kbuf; 370 char *tmp; 371 372 if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) { 373 rv = -ENOMEM; 374 goto out; 375 } 376 if (copy_from_user(kbuf, buf, count)) { 377 rv = -EFAULT; 378 goto out; 379 } 380 381 kbuf[count] = '\0'; 382 383 tmp = strchr(kbuf, ' '); 384 if (!tmp) { 385 rv = -EINVAL; 386 goto out; 387 } 388 *tmp = '\0'; 389 tmp++; 390 391 if (!strcmp(kbuf, "add_node")) 392 rv = do_add_node(tmp, count - (tmp - kbuf)); 393 else if (!strcmp(kbuf, "remove_node")) 394 rv = do_remove_node(tmp); 395 else 396 rv = -EINVAL; 397 out: 398 kfree(kbuf); 399 return rv ? rv : count; 400 } 401 402 static struct file_operations ofdt_fops = { 403 .write = ofdt_write 404 }; 405 406 /* create /proc/ppc64/ofdt write-only by root */ 407 static int proc_ppc64_create_ofdt(void) 408 { 409 struct proc_dir_entry *ent; 410 411 if (!platform_is_pseries()) 412 return 0; 413 414 ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL); 415 if (ent) { 416 ent->nlink = 1; 417 ent->data = NULL; 418 ent->size = 0; 419 ent->proc_fops = &ofdt_fops; 420 } 421 422 return 0; 423 } 424 __initcall(proc_ppc64_create_ofdt); 425