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