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