Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * ipmi_si_hotmod.c
0004  *
0005  * Handling for dynamically adding/removing IPMI devices through
0006  * a module parameter (and thus sysfs).
0007  */
0008 
0009 #define pr_fmt(fmt) "ipmi_hotmod: " fmt
0010 
0011 #include <linux/moduleparam.h>
0012 #include <linux/ipmi.h>
0013 #include <linux/atomic.h>
0014 #include "ipmi_si.h"
0015 #include "ipmi_plat_data.h"
0016 
0017 static int hotmod_handler(const char *val, const struct kernel_param *kp);
0018 
0019 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
0020 MODULE_PARM_DESC(hotmod,
0021          "Add and remove interfaces.  See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");
0022 
0023 /*
0024  * Parms come in as <op1>[:op2[:op3...]].  ops are:
0025  *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
0026  * Options are:
0027  *   rsp=<regspacing>
0028  *   rsi=<regsize>
0029  *   rsh=<regshift>
0030  *   irq=<irq>
0031  *   ipmb=<ipmb addr>
0032  */
0033 enum hotmod_op { HM_ADD, HM_REMOVE };
0034 struct hotmod_vals {
0035     const char *name;
0036     const int  val;
0037 };
0038 
0039 static const struct hotmod_vals hotmod_ops[] = {
0040     { "add",    HM_ADD },
0041     { "remove", HM_REMOVE },
0042     { NULL }
0043 };
0044 
0045 static const struct hotmod_vals hotmod_si[] = {
0046     { "kcs",    SI_KCS },
0047     { "smic",   SI_SMIC },
0048     { "bt",     SI_BT },
0049     { NULL }
0050 };
0051 
0052 static const struct hotmod_vals hotmod_as[] = {
0053     { "mem",    IPMI_MEM_ADDR_SPACE },
0054     { "i/o",    IPMI_IO_ADDR_SPACE },
0055     { NULL }
0056 };
0057 
0058 static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
0059              const char **curr)
0060 {
0061     char *s;
0062     int  i;
0063 
0064     s = strchr(*curr, ',');
0065     if (!s) {
0066         pr_warn("No hotmod %s given\n", name);
0067         return -EINVAL;
0068     }
0069     *s = '\0';
0070     s++;
0071     for (i = 0; v[i].name; i++) {
0072         if (strcmp(*curr, v[i].name) == 0) {
0073             *val = v[i].val;
0074             *curr = s;
0075             return 0;
0076         }
0077     }
0078 
0079     pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
0080     return -EINVAL;
0081 }
0082 
0083 static int check_hotmod_int_op(const char *curr, const char *option,
0084                    const char *name, unsigned int *val)
0085 {
0086     char *n;
0087 
0088     if (strcmp(curr, name) == 0) {
0089         if (!option) {
0090             pr_warn("No option given for '%s'\n", curr);
0091             return -EINVAL;
0092         }
0093         *val = simple_strtoul(option, &n, 0);
0094         if ((*n != '\0') || (*option == '\0')) {
0095             pr_warn("Bad option given for '%s'\n", curr);
0096             return -EINVAL;
0097         }
0098         return 1;
0099     }
0100     return 0;
0101 }
0102 
0103 static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
0104                 struct ipmi_plat_data *h)
0105 {
0106     char *s, *o;
0107     int rv;
0108     unsigned int ival;
0109 
0110     h->iftype = IPMI_PLAT_IF_SI;
0111     rv = parse_str(hotmod_ops, &ival, "operation", &curr);
0112     if (rv)
0113         return rv;
0114     *op = ival;
0115 
0116     rv = parse_str(hotmod_si, &ival, "interface type", &curr);
0117     if (rv)
0118         return rv;
0119     h->type = ival;
0120 
0121     rv = parse_str(hotmod_as, &ival, "address space", &curr);
0122     if (rv)
0123         return rv;
0124     h->space = ival;
0125 
0126     s = strchr(curr, ',');
0127     if (s) {
0128         *s = '\0';
0129         s++;
0130     }
0131     rv = kstrtoul(curr, 0, &h->addr);
0132     if (rv) {
0133         pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
0134         return rv;
0135     }
0136 
0137     while (s) {
0138         curr = s;
0139         s = strchr(curr, ',');
0140         if (s) {
0141             *s = '\0';
0142             s++;
0143         }
0144         o = strchr(curr, '=');
0145         if (o) {
0146             *o = '\0';
0147             o++;
0148         }
0149         rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
0150         if (rv < 0)
0151             return rv;
0152         else if (rv)
0153             continue;
0154         rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
0155         if (rv < 0)
0156             return rv;
0157         else if (rv)
0158             continue;
0159         rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
0160         if (rv < 0)
0161             return rv;
0162         else if (rv)
0163             continue;
0164         rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
0165         if (rv < 0)
0166             return rv;
0167         else if (rv)
0168             continue;
0169         rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
0170         if (rv < 0)
0171             return rv;
0172         else if (rv)
0173             continue;
0174 
0175         pr_warn("Invalid hotmod option '%s'\n", curr);
0176         return -EINVAL;
0177     }
0178 
0179     h->addr_source = SI_HOTMOD;
0180     return 0;
0181 }
0182 
0183 static atomic_t hotmod_nr;
0184 
0185 static int hotmod_handler(const char *val, const struct kernel_param *kp)
0186 {
0187     int  rv;
0188     struct ipmi_plat_data h;
0189     char *str, *curr, *next;
0190 
0191     str = kstrdup(val, GFP_KERNEL);
0192     if (!str)
0193         return -ENOMEM;
0194 
0195     /* Kill any trailing spaces, as we can get a "\n" from echo. */
0196     for (curr = strstrip(str); curr; curr = next) {
0197         enum hotmod_op op;
0198 
0199         next = strchr(curr, ':');
0200         if (next) {
0201             *next = '\0';
0202             next++;
0203         }
0204 
0205         memset(&h, 0, sizeof(h));
0206         rv = parse_hotmod_str(curr, &op, &h);
0207         if (rv)
0208             goto out;
0209 
0210         if (op == HM_ADD) {
0211             ipmi_platform_add("hotmod-ipmi-si",
0212                       atomic_inc_return(&hotmod_nr),
0213                       &h);
0214         } else {
0215             struct device *dev;
0216 
0217             dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
0218             if (dev && dev_is_platform(dev)) {
0219                 struct platform_device *pdev;
0220 
0221                 pdev = to_platform_device(dev);
0222                 if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
0223                     platform_device_unregister(pdev);
0224             }
0225             put_device(dev);
0226         }
0227     }
0228     rv = strlen(val);
0229 out:
0230     kfree(str);
0231     return rv;
0232 }
0233 
0234 void ipmi_si_hotmod_exit(void)
0235 {
0236     ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
0237 }