cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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);