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