]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
patches for 4.19
authorSasha Levin <sashal@kernel.org>
Wed, 27 Mar 2019 18:31:15 +0000 (14:31 -0400)
committerSasha Levin <sashal@kernel.org>
Wed, 27 Mar 2019 18:42:07 +0000 (14:42 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.19/ipmi_si-fix-crash-when-using-hard-coded-device.patch [new file with mode: 0644]
queue-4.19/series

diff --git a/queue-4.19/ipmi_si-fix-crash-when-using-hard-coded-device.patch b/queue-4.19/ipmi_si-fix-crash-when-using-hard-coded-device.patch
new file mode 100644 (file)
index 0000000..7bee242
--- /dev/null
@@ -0,0 +1,497 @@
+From e60de9df336c3e715143b3d1c04885b0ec69e9c1 Mon Sep 17 00:00:00 2001
+From: Corey Minyard <cminyard@mvista.com>
+Date: Tue, 26 Mar 2019 11:10:04 -0500
+Subject: ipmi_si: Fix crash when using hard-coded device
+
+Backport from 41b766d661bf94a364960862cfc248a78313dbd3
+
+When excuting a command like:
+  modprobe ipmi_si ports=0xffc0e3 type=bt
+The system would get an oops.
+
+The trouble here is that ipmi_si_hardcode_find_bmc() is called before
+ipmi_si_platform_init(), but initialization of the hard-coded device
+creates an IPMI platform device, which won't be initialized yet.
+
+The real trouble is that hard-coded devices aren't created with
+any device, and the fixup is done later.  So do it right, create the
+hard-coded devices as normal platform devices.
+
+This required adding some new resource types to the IPMI platform
+code for passing information required by the hard-coded device
+and adding some code to remove the hard-coded platform devices
+on module removal.
+
+To enforce the "hard-coded devices passed by the user take priority
+over firmware devices" rule, some special code was added to check
+and see if a hard-coded device already exists.
+
+The backport required some minor fixups and adding the device
+id table that had been added in another change and was used
+in this one.
+
+Reported-by: Yang Yingliang <yangyingliang@huawei.com>
+Cc: stable@vger.kernel.org # v4.15+
+Signed-off-by: Corey Minyard <cminyard@mvista.com>
+Tested-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/char/ipmi/ipmi_si.h          |   4 +-
+ drivers/char/ipmi/ipmi_si_hardcode.c | 236 ++++++++++++++++++++-------
+ drivers/char/ipmi/ipmi_si_intf.c     |  22 ++-
+ drivers/char/ipmi/ipmi_si_platform.c |  30 +++-
+ 4 files changed, 218 insertions(+), 74 deletions(-)
+
+diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
+index 52f6152d1fcb..7ae52c17618e 100644
+--- a/drivers/char/ipmi/ipmi_si.h
++++ b/drivers/char/ipmi/ipmi_si.h
+@@ -25,7 +25,9 @@ void ipmi_irq_finish_setup(struct si_sm_io *io);
+ int ipmi_si_remove_by_dev(struct device *dev);
+ void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+                           unsigned long addr);
+-int ipmi_si_hardcode_find_bmc(void);
++void ipmi_hardcode_init(void);
++void ipmi_si_hardcode_exit(void);
++int ipmi_si_hardcode_match(int addr_type, unsigned long addr);
+ void ipmi_si_platform_init(void);
+ void ipmi_si_platform_shutdown(void);
+diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
+index 10219f24546b..9ae2405c28bb 100644
+--- a/drivers/char/ipmi/ipmi_si_hardcode.c
++++ b/drivers/char/ipmi/ipmi_si_hardcode.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ #include <linux/moduleparam.h>
++#include <linux/platform_device.h>
+ #include "ipmi_si.h"
+ #define PFX "ipmi_hardcode: "
+@@ -11,23 +12,22 @@
+ #define SI_MAX_PARMS 4
+-static char          *si_type[SI_MAX_PARMS];
+ #define MAX_SI_TYPE_STR 30
+-static char          si_type_str[MAX_SI_TYPE_STR];
++static char          si_type_str[MAX_SI_TYPE_STR] __initdata;
+ static unsigned long addrs[SI_MAX_PARMS];
+ static unsigned int num_addrs;
+ static unsigned int  ports[SI_MAX_PARMS];
+ static unsigned int num_ports;
+-static int           irqs[SI_MAX_PARMS];
+-static unsigned int num_irqs;
+-static int           regspacings[SI_MAX_PARMS];
+-static unsigned int num_regspacings;
+-static int           regsizes[SI_MAX_PARMS];
+-static unsigned int num_regsizes;
+-static int           regshifts[SI_MAX_PARMS];
+-static unsigned int num_regshifts;
+-static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */
+-static unsigned int num_slave_addrs;
++static int           irqs[SI_MAX_PARMS] __initdata;
++static unsigned int num_irqs __initdata;
++static int           regspacings[SI_MAX_PARMS] __initdata;
++static unsigned int num_regspacings __initdata;
++static int           regsizes[SI_MAX_PARMS] __initdata;
++static unsigned int num_regsizes __initdata;
++static int           regshifts[SI_MAX_PARMS] __initdata;
++static unsigned int num_regshifts __initdata;
++static int slave_addrs[SI_MAX_PARMS] __initdata;
++static unsigned int num_slave_addrs __initdata;
+ module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
+ MODULE_PARM_DESC(type, "Defines the type of each interface, each"
+@@ -72,12 +72,133 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
+                " overridden by this parm.  This is an array indexed"
+                " by interface number.");
+-int ipmi_si_hardcode_find_bmc(void)
++static struct platform_device *ipmi_hc_pdevs[SI_MAX_PARMS];
++
++static void __init ipmi_hardcode_init_one(const char *si_type_str,
++                                        unsigned int i,
++                                        unsigned long addr,
++                                        unsigned int flags)
+ {
+-      int ret = -ENODEV;
+-      int             i;
+-      struct si_sm_io io;
++      struct platform_device *pdev;
++      unsigned int num_r = 1, size;
++      struct resource r[4];
++      struct property_entry p[6];
++      enum si_type si_type;
++      unsigned int regspacing, regsize;
++      int rv;
++
++      memset(p, 0, sizeof(p));
++      memset(r, 0, sizeof(r));
++
++      if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) {
++              size = 2;
++              si_type = SI_KCS;
++      } else if (strcmp(si_type_str, "smic") == 0) {
++              size = 2;
++              si_type = SI_SMIC;
++      } else if (strcmp(si_type_str, "bt") == 0) {
++              size = 3;
++              si_type = SI_BT;
++      } else if (strcmp(si_type_str, "invalid") == 0) {
++              /*
++               * Allow a firmware-specified interface to be
++               * disabled.
++               */
++              size = 1;
++              si_type = SI_TYPE_INVALID;
++      } else {
++              pr_warn("Interface type specified for interface %d, was invalid: %s\n",
++                      i, si_type_str);
++              return;
++      }
++
++      regsize = regsizes[i];
++      if (regsize == 0)
++              regsize = DEFAULT_REGSIZE;
++
++      p[0] = PROPERTY_ENTRY_U8("ipmi-type", si_type);
++      p[1] = PROPERTY_ENTRY_U8("slave-addr", slave_addrs[i]);
++      p[2] = PROPERTY_ENTRY_U8("addr-source", SI_HARDCODED);
++      p[3] = PROPERTY_ENTRY_U8("reg-shift", regshifts[i]);
++      p[4] = PROPERTY_ENTRY_U8("reg-size", regsize);
++      /* Last entry must be left NULL to terminate it. */
++
++      /*
++       * Register spacing is derived from the resources in
++       * the IPMI platform code.
++       */
++      regspacing = regspacings[i];
++      if (regspacing == 0)
++              regspacing = regsize;
++
++      r[0].start = addr;
++      r[0].end = r[0].start + regsize - 1;
++      r[0].name = "IPMI Address 1";
++      r[0].flags = flags;
++
++      if (size > 1) {
++              r[1].start = r[0].start + regspacing;
++              r[1].end = r[1].start + regsize - 1;
++              r[1].name = "IPMI Address 2";
++              r[1].flags = flags;
++              num_r++;
++      }
++
++      if (size > 2) {
++              r[2].start = r[1].start + regspacing;
++              r[2].end = r[2].start + regsize - 1;
++              r[2].name = "IPMI Address 3";
++              r[2].flags = flags;
++              num_r++;
++      }
++
++      if (irqs[i]) {
++              r[num_r].start = irqs[i];
++              r[num_r].end = irqs[i];
++              r[num_r].name = "IPMI IRQ";
++              r[num_r].flags = IORESOURCE_IRQ;
++              num_r++;
++      }
++
++      pdev = platform_device_alloc("hardcode-ipmi-si", i);
++      if (!pdev) {
++              pr_err("Error allocating IPMI platform device %d\n", i);
++              return;
++      }
++
++      rv = platform_device_add_resources(pdev, r, num_r);
++      if (rv) {
++              dev_err(&pdev->dev,
++                      "Unable to add hard-code resources: %d\n", rv);
++              goto err;
++      }
++
++      rv = platform_device_add_properties(pdev, p);
++      if (rv) {
++              dev_err(&pdev->dev,
++                      "Unable to add hard-code properties: %d\n", rv);
++              goto err;
++      }
++
++      rv = platform_device_add(pdev);
++      if (rv) {
++              dev_err(&pdev->dev,
++                      "Unable to add hard-code device: %d\n", rv);
++              goto err;
++      }
++
++      ipmi_hc_pdevs[i] = pdev;
++      return;
++
++err:
++      platform_device_put(pdev);
++}
++
++void __init ipmi_hardcode_init(void)
++{
++      unsigned int i;
+       char *str;
++      char *si_type[SI_MAX_PARMS];
+       /* Parse out the si_type string into its components. */
+       str = si_type_str;
+@@ -94,54 +215,45 @@ int ipmi_si_hardcode_find_bmc(void)
+               }
+       }
+-      memset(&io, 0, sizeof(io));
+       for (i = 0; i < SI_MAX_PARMS; i++) {
+-              if (!ports[i] && !addrs[i])
+-                      continue;
+-
+-              io.addr_source = SI_HARDCODED;
+-              pr_info(PFX "probing via hardcoded address\n");
+-
+-              if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
+-                      io.si_type = SI_KCS;
+-              } else if (strcmp(si_type[i], "smic") == 0) {
+-                      io.si_type = SI_SMIC;
+-              } else if (strcmp(si_type[i], "bt") == 0) {
+-                      io.si_type = SI_BT;
+-              } else {
+-                      pr_warn(PFX "Interface type specified for interface %d, was invalid: %s\n",
+-                              i, si_type[i]);
+-                      continue;
+-              }
++              if (i < num_ports && ports[i])
++                      ipmi_hardcode_init_one(si_type[i], i, ports[i],
++                                             IORESOURCE_IO);
++              if (i < num_addrs && addrs[i])
++                      ipmi_hardcode_init_one(si_type[i], i, addrs[i],
++                                             IORESOURCE_MEM);
++      }
++}
+-              if (ports[i]) {
+-                      /* An I/O port */
+-                      io.addr_data = ports[i];
+-                      io.addr_type = IPMI_IO_ADDR_SPACE;
+-              } else if (addrs[i]) {
+-                      /* A memory port */
+-                      io.addr_data = addrs[i];
+-                      io.addr_type = IPMI_MEM_ADDR_SPACE;
+-              } else {
+-                      pr_warn(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n",
+-                              i);
+-                      continue;
+-              }
++void ipmi_si_hardcode_exit(void)
++{
++      unsigned int i;
+-              io.addr = NULL;
+-              io.regspacing = regspacings[i];
+-              if (!io.regspacing)
+-                      io.regspacing = DEFAULT_REGSPACING;
+-              io.regsize = regsizes[i];
+-              if (!io.regsize)
+-                      io.regsize = DEFAULT_REGSIZE;
+-              io.regshift = regshifts[i];
+-              io.irq = irqs[i];
+-              if (io.irq)
+-                      io.irq_setup = ipmi_std_irq_setup;
+-              io.slave_addr = slave_addrs[i];
+-
+-              ret = ipmi_si_add_smi(&io);
++      for (i = 0; i < SI_MAX_PARMS; i++) {
++              if (ipmi_hc_pdevs[i])
++                      platform_device_unregister(ipmi_hc_pdevs[i]);
+       }
+-      return ret;
++}
++
++/*
++ * Returns true of the given address exists as a hardcoded address,
++ * false if not.
++ */
++int ipmi_si_hardcode_match(int addr_type, unsigned long addr)
++{
++      unsigned int i;
++
++      if (addr_type == IPMI_IO_ADDR_SPACE) {
++              for (i = 0; i < num_ports; i++) {
++                      if (ports[i] == addr)
++                              return 1;
++              }
++      } else {
++              for (i = 0; i < num_addrs; i++) {
++                      if (addrs[i] == addr)
++                              return 1;
++              }
++      }
++
++      return 0;
+ }
+diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
+index 82d831b103f9..75e5006f395a 100644
+--- a/drivers/char/ipmi/ipmi_si_intf.c
++++ b/drivers/char/ipmi/ipmi_si_intf.c
+@@ -1862,6 +1862,18 @@ int ipmi_si_add_smi(struct si_sm_io *io)
+       int rv = 0;
+       struct smi_info *new_smi, *dup;
++      /*
++       * If the user gave us a hard-coded device at the same
++       * address, they presumably want us to use it and not what is
++       * in the firmware.
++       */
++      if (io->addr_source != SI_HARDCODED &&
++          ipmi_si_hardcode_match(io->addr_type, io->addr_data)) {
++              dev_info(io->dev,
++                       "Hard-coded device at this address already exists");
++              return -ENODEV;
++      }
++
+       if (!io->io_setup) {
+               if (io->addr_type == IPMI_IO_ADDR_SPACE) {
+                       io->io_setup = ipmi_si_port_setup;
+@@ -2094,7 +2106,7 @@ static int try_smi_init(struct smi_info *new_smi)
+       return rv;
+ }
+-static int init_ipmi_si(void)
++static int __init init_ipmi_si(void)
+ {
+       struct smi_info *e;
+       enum ipmi_addr_src type = SI_INVALID;
+@@ -2102,12 +2114,9 @@ static int init_ipmi_si(void)
+       if (initialized)
+               return 0;
++      ipmi_hardcode_init();
+       pr_info("IPMI System Interface driver.\n");
+-      /* If the user gave us a device, they presumably want us to use it */
+-      if (!ipmi_si_hardcode_find_bmc())
+-              goto do_scan;
+-
+       ipmi_si_platform_init();
+       ipmi_si_pci_init();
+@@ -2118,7 +2127,6 @@ static int init_ipmi_si(void)
+          with multiple BMCs we assume that there will be several instances
+          of a given type so if we succeed in registering a type then also
+          try to register everything else of the same type */
+-do_scan:
+       mutex_lock(&smi_infos_lock);
+       list_for_each_entry(e, &smi_infos, link) {
+               /* Try to register a device if it has an IRQ and we either
+@@ -2304,6 +2312,8 @@ static void cleanup_ipmi_si(void)
+       list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
+               cleanup_one_si(e);
+       mutex_unlock(&smi_infos_lock);
++
++      ipmi_si_hardcode_exit();
+ }
+ module_exit(cleanup_ipmi_si);
+diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
+index bf69927502bd..d32b0dd377c5 100644
+--- a/drivers/char/ipmi/ipmi_si_platform.c
++++ b/drivers/char/ipmi/ipmi_si_platform.c
+@@ -126,8 +126,6 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
+               if (res_second->start > io->addr_data)
+                       io->regspacing = res_second->start - io->addr_data;
+       }
+-      io->regsize = DEFAULT_REGSIZE;
+-      io->regshift = 0;
+       return res;
+ }
+@@ -135,7 +133,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
+ static int platform_ipmi_probe(struct platform_device *pdev)
+ {
+       struct si_sm_io io;
+-      u8 type, slave_addr, addr_source;
++      u8 type, slave_addr, addr_source, regsize, regshift;
+       int rv;
+       rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
+@@ -147,7 +145,7 @@ static int platform_ipmi_probe(struct platform_device *pdev)
+       if (addr_source == SI_SMBIOS) {
+               if (!si_trydmi)
+                       return -ENODEV;
+-      } else {
++      } else if (addr_source != SI_HARDCODED) {
+               if (!si_tryplatform)
+                       return -ENODEV;
+       }
+@@ -167,11 +165,23 @@ static int platform_ipmi_probe(struct platform_device *pdev)
+       case SI_BT:
+               io.si_type = type;
+               break;
++      case SI_TYPE_INVALID: /* User disabled this in hardcode. */
++              return -ENODEV;
+       default:
+               dev_err(&pdev->dev, "ipmi-type property is invalid\n");
+               return -EINVAL;
+       }
++      io.regsize = DEFAULT_REGSIZE;
++      rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
++      if (!rv)
++              io.regsize = regsize;
++
++      io.regshift = 0;
++      rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
++      if (!rv)
++              io.regshift = regshift;
++
+       if (!ipmi_get_info_from_resources(pdev, &io))
+               return -EINVAL;
+@@ -191,7 +201,8 @@ static int platform_ipmi_probe(struct platform_device *pdev)
+       io.dev = &pdev->dev;
+-      pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
++      pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
++              ipmi_addr_src_to_str(addr_source),
+               (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+               io.addr_data, io.regsize, io.regspacing, io.irq);
+@@ -356,6 +367,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
+               goto err_free;
+       }
++      io.regsize = DEFAULT_REGSIZE;
++      io.regshift = 0;
++
+       res = ipmi_get_info_from_resources(pdev, &io);
+       if (!res) {
+               rv = -EINVAL;
+@@ -417,6 +431,11 @@ static int ipmi_remove(struct platform_device *pdev)
+       return ipmi_si_remove_by_dev(&pdev->dev);
+ }
++static const struct platform_device_id si_plat_ids[] = {
++    { "hardcode-ipmi-si", 0 },
++    { }
++};
++
+ struct platform_driver ipmi_platform_driver = {
+       .driver = {
+               .name = DEVICE_NAME,
+@@ -425,6 +444,7 @@ struct platform_driver ipmi_platform_driver = {
+       },
+       .probe          = ipmi_probe,
+       .remove         = ipmi_remove,
++      .id_table       = si_plat_ids
+ };
+ void ipmi_si_platform_init(void)
+-- 
+2.19.1
+
index 03f59929534d0e4270c78611608823ab8196ba01..d27d9d38b3e9c423362d8d075dd689867ad38b0c 100644 (file)
@@ -1,2 +1,3 @@
 bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch
 bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch
+ipmi_si-fix-crash-when-using-hard-coded-device.patch