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

ipmi_si_hotmod.c (4789B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * ipmi_si_hotmod.c
      4 *
      5 * Handling for dynamically adding/removing IPMI devices through
      6 * a module parameter (and thus sysfs).
      7 */
      8
      9#define pr_fmt(fmt) "ipmi_hotmod: " fmt
     10
     11#include <linux/moduleparam.h>
     12#include <linux/ipmi.h>
     13#include <linux/atomic.h>
     14#include "ipmi_si.h"
     15#include "ipmi_plat_data.h"
     16
     17static int hotmod_handler(const char *val, const struct kernel_param *kp);
     18
     19module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
     20MODULE_PARM_DESC(hotmod,
     21		 "Add and remove interfaces.  See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");
     22
     23/*
     24 * Parms come in as <op1>[:op2[:op3...]].  ops are:
     25 *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
     26 * Options are:
     27 *   rsp=<regspacing>
     28 *   rsi=<regsize>
     29 *   rsh=<regshift>
     30 *   irq=<irq>
     31 *   ipmb=<ipmb addr>
     32 */
     33enum hotmod_op { HM_ADD, HM_REMOVE };
     34struct hotmod_vals {
     35	const char *name;
     36	const int  val;
     37};
     38
     39static const struct hotmod_vals hotmod_ops[] = {
     40	{ "add",	HM_ADD },
     41	{ "remove",	HM_REMOVE },
     42	{ NULL }
     43};
     44
     45static const struct hotmod_vals hotmod_si[] = {
     46	{ "kcs",	SI_KCS },
     47	{ "smic",	SI_SMIC },
     48	{ "bt",		SI_BT },
     49	{ NULL }
     50};
     51
     52static const struct hotmod_vals hotmod_as[] = {
     53	{ "mem",	IPMI_MEM_ADDR_SPACE },
     54	{ "i/o",	IPMI_IO_ADDR_SPACE },
     55	{ NULL }
     56};
     57
     58static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
     59		     const char **curr)
     60{
     61	char *s;
     62	int  i;
     63
     64	s = strchr(*curr, ',');
     65	if (!s) {
     66		pr_warn("No hotmod %s given\n", name);
     67		return -EINVAL;
     68	}
     69	*s = '\0';
     70	s++;
     71	for (i = 0; v[i].name; i++) {
     72		if (strcmp(*curr, v[i].name) == 0) {
     73			*val = v[i].val;
     74			*curr = s;
     75			return 0;
     76		}
     77	}
     78
     79	pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
     80	return -EINVAL;
     81}
     82
     83static int check_hotmod_int_op(const char *curr, const char *option,
     84			       const char *name, unsigned int *val)
     85{
     86	char *n;
     87
     88	if (strcmp(curr, name) == 0) {
     89		if (!option) {
     90			pr_warn("No option given for '%s'\n", curr);
     91			return -EINVAL;
     92		}
     93		*val = simple_strtoul(option, &n, 0);
     94		if ((*n != '\0') || (*option == '\0')) {
     95			pr_warn("Bad option given for '%s'\n", curr);
     96			return -EINVAL;
     97		}
     98		return 1;
     99	}
    100	return 0;
    101}
    102
    103static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
    104			    struct ipmi_plat_data *h)
    105{
    106	char *s, *o;
    107	int rv;
    108	unsigned int ival;
    109
    110	h->iftype = IPMI_PLAT_IF_SI;
    111	rv = parse_str(hotmod_ops, &ival, "operation", &curr);
    112	if (rv)
    113		return rv;
    114	*op = ival;
    115
    116	rv = parse_str(hotmod_si, &ival, "interface type", &curr);
    117	if (rv)
    118		return rv;
    119	h->type = ival;
    120
    121	rv = parse_str(hotmod_as, &ival, "address space", &curr);
    122	if (rv)
    123		return rv;
    124	h->space = ival;
    125
    126	s = strchr(curr, ',');
    127	if (s) {
    128		*s = '\0';
    129		s++;
    130	}
    131	rv = kstrtoul(curr, 0, &h->addr);
    132	if (rv) {
    133		pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
    134		return rv;
    135	}
    136
    137	while (s) {
    138		curr = s;
    139		s = strchr(curr, ',');
    140		if (s) {
    141			*s = '\0';
    142			s++;
    143		}
    144		o = strchr(curr, '=');
    145		if (o) {
    146			*o = '\0';
    147			o++;
    148		}
    149		rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
    150		if (rv < 0)
    151			return rv;
    152		else if (rv)
    153			continue;
    154		rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
    155		if (rv < 0)
    156			return rv;
    157		else if (rv)
    158			continue;
    159		rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
    160		if (rv < 0)
    161			return rv;
    162		else if (rv)
    163			continue;
    164		rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
    165		if (rv < 0)
    166			return rv;
    167		else if (rv)
    168			continue;
    169		rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
    170		if (rv < 0)
    171			return rv;
    172		else if (rv)
    173			continue;
    174
    175		pr_warn("Invalid hotmod option '%s'\n", curr);
    176		return -EINVAL;
    177	}
    178
    179	h->addr_source = SI_HOTMOD;
    180	return 0;
    181}
    182
    183static atomic_t hotmod_nr;
    184
    185static int hotmod_handler(const char *val, const struct kernel_param *kp)
    186{
    187	int  rv;
    188	struct ipmi_plat_data h;
    189	char *str, *curr, *next;
    190
    191	str = kstrdup(val, GFP_KERNEL);
    192	if (!str)
    193		return -ENOMEM;
    194
    195	/* Kill any trailing spaces, as we can get a "\n" from echo. */
    196	for (curr = strstrip(str); curr; curr = next) {
    197		enum hotmod_op op;
    198
    199		next = strchr(curr, ':');
    200		if (next) {
    201			*next = '\0';
    202			next++;
    203		}
    204
    205		memset(&h, 0, sizeof(h));
    206		rv = parse_hotmod_str(curr, &op, &h);
    207		if (rv)
    208			goto out;
    209
    210		if (op == HM_ADD) {
    211			ipmi_platform_add("hotmod-ipmi-si",
    212					  atomic_inc_return(&hotmod_nr),
    213					  &h);
    214		} else {
    215			struct device *dev;
    216
    217			dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
    218			if (dev && dev_is_platform(dev)) {
    219				struct platform_device *pdev;
    220
    221				pdev = to_platform_device(dev);
    222				if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
    223					platform_device_unregister(pdev);
    224			}
    225			put_device(dev);
    226		}
    227	}
    228	rv = strlen(val);
    229out:
    230	kfree(str);
    231	return rv;
    232}
    233
    234void ipmi_si_hotmod_exit(void)
    235{
    236	ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
    237}