ipmi_si_hotmod.c 4.7 KB

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