summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ipmi/Kconfig4
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c273
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h12
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c9
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c265
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c176
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c7
-rw-r--r--drivers/char/random.c101
9 files changed, 614 insertions, 234 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 90f3edffb067..f6fa056a52fc 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -5,6 +5,7 @@
menuconfig IPMI_HANDLER
tristate 'IPMI top-level message handler'
depends on HAS_IOMEM
+ select IPMI_DMI_DECODE if DMI
help
This enables the central IPMI message handler, required for IPMI
to work.
@@ -16,6 +17,9 @@ menuconfig IPMI_HANDLER
If unsure, say N.
+config IPMI_DMI_DECODE
+ bool
+
if IPMI_HANDLER
config IPMI_PANIC_EVENT
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 0d98cd91def1..eefb0b301e83 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -7,6 +7,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
+obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
new file mode 100644
index 000000000000..2a84401dea05
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -0,0 +1,273 @@
+/*
+ * A hack to create a platform device from a DMI entry. This will
+ * allow autoloading of the IPMI drive based on SMBIOS entries.
+ */
+
+#include <linux/ipmi.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include "ipmi_dmi.h"
+
+struct ipmi_dmi_info {
+ int type;
+ u32 flags;
+ unsigned long addr;
+ u8 slave_addr;
+ struct ipmi_dmi_info *next;
+};
+
+static struct ipmi_dmi_info *ipmi_dmi_infos;
+
+static int ipmi_dmi_nr __initdata;
+
+static void __init dmi_add_platform_ipmi(unsigned long base_addr,
+ u32 flags,
+ u8 slave_addr,
+ int irq,
+ int offset,
+ int type)
+{
+ struct platform_device *pdev;
+ struct resource r[4];
+ unsigned int num_r = 1, size;
+ struct property_entry p[4] = {
+ PROPERTY_ENTRY_U8("slave-addr", slave_addr),
+ PROPERTY_ENTRY_U8("ipmi-type", type),
+ PROPERTY_ENTRY_U16("i2c-addr", base_addr),
+ { }
+ };
+ char *name, *override;
+ int rv;
+ struct ipmi_dmi_info *info;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ pr_warn("ipmi:dmi: Could not allocate dmi info\n");
+ } else {
+ info->type = type;
+ info->flags = flags;
+ info->addr = base_addr;
+ info->slave_addr = slave_addr;
+ info->next = ipmi_dmi_infos;
+ ipmi_dmi_infos = info;
+ }
+
+ name = "dmi-ipmi-si";
+ override = "ipmi_si";
+ switch (type) {
+ case IPMI_DMI_TYPE_SSIF:
+ name = "dmi-ipmi-ssif";
+ override = "ipmi_ssif";
+ offset = 1;
+ size = 1;
+ break;
+ case IPMI_DMI_TYPE_BT:
+ size = 3;
+ break;
+ case IPMI_DMI_TYPE_KCS:
+ case IPMI_DMI_TYPE_SMIC:
+ size = 2;
+ break;
+ default:
+ pr_err("ipmi:dmi: Invalid IPMI type: %d", type);
+ return;
+ }
+
+ pdev = platform_device_alloc(name, ipmi_dmi_nr);
+ if (!pdev) {
+ pr_err("ipmi:dmi: Error allocation IPMI platform device");
+ return;
+ }
+ pdev->driver_override = override;
+
+ if (type == IPMI_DMI_TYPE_SSIF)
+ goto add_properties;
+
+ memset(r, 0, sizeof(r));
+
+ r[0].start = base_addr;
+ r[0].end = r[0].start + offset - 1;
+ r[0].name = "IPMI Address 1";
+ r[0].flags = flags;
+
+ if (size > 1) {
+ r[1].start = r[0].start + offset;
+ r[1].end = r[1].start + offset - 1;
+ r[1].name = "IPMI Address 2";
+ r[1].flags = flags;
+ num_r++;
+ }
+
+ if (size > 2) {
+ r[2].start = r[1].start + offset;
+ r[2].end = r[2].start + offset - 1;
+ r[2].name = "IPMI Address 3";
+ r[2].flags = flags;
+ num_r++;
+ }
+
+ if (irq) {
+ r[num_r].start = irq;
+ r[num_r].end = irq;
+ r[num_r].name = "IPMI IRQ";
+ r[num_r].flags = IORESOURCE_IRQ;
+ num_r++;
+ }
+
+ rv = platform_device_add_resources(pdev, r, num_r);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "ipmi:dmi: Unable to add resources: %d\n", rv);
+ goto err;
+ }
+
+add_properties:
+ rv = platform_device_add_properties(pdev, p);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "ipmi:dmi: Unable to add properties: %d\n", rv);
+ goto err;
+ }
+
+ rv = platform_device_add(pdev);
+ if (rv) {
+ dev_err(&pdev->dev, "ipmi:dmi: Unable to add device: %d\n", rv);
+ goto err;
+ }
+
+ ipmi_dmi_nr++;
+ return;
+
+err:
+ platform_device_put(pdev);
+}
+
+/*
+ * Look up the slave address for a given interface. This is here
+ * because ACPI doesn't have a slave address while SMBIOS does, but we
+ * prefer using ACPI so the ACPI code can use the IPMI namespace.
+ * This function allows an ACPI-specified IPMI device to look up the
+ * slave address from the DMI table.
+ */
+int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr)
+{
+ struct ipmi_dmi_info *info = ipmi_dmi_infos;
+
+ while (info) {
+ if (info->type == type &&
+ info->flags == flags &&
+ info->addr == base_addr)
+ return info->slave_addr;
+ info = info->next;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipmi_dmi_get_slave_addr);
+
+#define DMI_IPMI_MIN_LENGTH 0x10
+#define DMI_IPMI_VER2_LENGTH 0x12
+#define DMI_IPMI_TYPE 4
+#define DMI_IPMI_SLAVEADDR 6
+#define DMI_IPMI_ADDR 8
+#define DMI_IPMI_ACCESS 0x10
+#define DMI_IPMI_IRQ 0x11
+#define DMI_IPMI_IO_MASK 0xfffe
+
+static void __init dmi_decode_ipmi(const struct dmi_header *dm)
+{
+ const u8 *data = (const u8 *) dm;
+ u32 flags = IORESOURCE_IO;
+ unsigned long base_addr;
+ u8 len = dm->length;
+ u8 slave_addr;
+ int irq = 0, offset;
+ int type;
+
+ if (len < DMI_IPMI_MIN_LENGTH)
+ return;
+
+ type = data[DMI_IPMI_TYPE];
+ slave_addr = data[DMI_IPMI_SLAVEADDR];
+
+ memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long));
+ if (len >= DMI_IPMI_VER2_LENGTH) {
+ if (type == IPMI_DMI_TYPE_SSIF) {
+ offset = 0;
+ flags = 0;
+ base_addr = data[DMI_IPMI_ADDR] >> 1;
+ if (base_addr == 0) {
+ /*
+ * Some broken systems put the I2C address in
+ * the slave address field. We try to
+ * accommodate them here.
+ */
+ base_addr = data[DMI_IPMI_SLAVEADDR] >> 1;
+ slave_addr = 0;
+ }
+ } else {
+ if (base_addr & 1) {
+ /* I/O */
+ base_addr &= DMI_IPMI_IO_MASK;
+ } else {
+ /* Memory */
+ flags = IORESOURCE_MEM;
+ }
+
+ /*
+ * If bit 4 of byte 0x10 is set, then the lsb
+ * for the address is odd.
+ */
+ base_addr |= (data[DMI_IPMI_ACCESS] >> 4) & 1;
+
+ irq = data[DMI_IPMI_IRQ];
+
+ /*
+ * The top two bits of byte 0x10 hold the
+ * register spacing.
+ */
+ switch ((data[DMI_IPMI_ACCESS] >> 6) & 3) {
+ case 0: /* Byte boundaries */
+ offset = 1;
+ break;
+ case 1: /* 32-bit boundaries */
+ offset = 4;
+ break;
+ case 2: /* 16-byte boundaries */
+ offset = 16;
+ break;
+ default:
+ pr_err("ipmi:dmi: Invalid offset: 0");
+ return;
+ }
+ }
+ } else {
+ /* Old DMI spec. */
+ /*
+ * Note that technically, the lower bit of the base
+ * address should be 1 if the address is I/O and 0 if
+ * the address is in memory. So many systems get that
+ * wrong (and all that I have seen are I/O) so we just
+ * ignore that bit and assume I/O. Systems that use
+ * memory should use the newer spec, anyway.
+ */
+ base_addr = base_addr & DMI_IPMI_IO_MASK;
+ offset = 1;
+ }
+
+ dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq,
+ offset, type);
+}
+
+static int __init scan_for_dmi_ipmi(void)
+{
+ const struct dmi_device *dev = NULL;
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
+ dmi_decode_ipmi((const struct dmi_header *) dev->device_data);
+
+ return 0;
+}
+subsys_initcall(scan_for_dmi_ipmi);
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
new file mode 100644
index 000000000000..0a1afe5ceb1e
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -0,0 +1,12 @@
+/*
+ * DMI defines for use by IPMI
+ */
+
+#define IPMI_DMI_TYPE_KCS 0x01
+#define IPMI_DMI_TYPE_SMIC 0x02
+#define IPMI_DMI_TYPE_BT 0x03
+#define IPMI_DMI_TYPE_SSIF 0x04
+
+#ifdef CONFIG_IPMI_DMI_DECODE
+int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr);
+#endif
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 9f699951b75a..810b138f5897 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -2397,7 +2397,7 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj,
return mode;
}
-static struct attribute_group bmc_dev_attr_group = {
+static const struct attribute_group bmc_dev_attr_group = {
.attrs = bmc_dev_attrs,
.is_visible = bmc_dev_attr_is_visible,
};
@@ -2407,7 +2407,7 @@ static const struct attribute_group *bmc_dev_attr_groups[] = {
NULL
};
-static struct device_type bmc_device_type = {
+static const struct device_type bmc_device_type = {
.groups = bmc_dev_attr_groups,
};
@@ -3878,6 +3878,9 @@ static void smi_recv_tasklet(unsigned long val)
* because the lower layer is allowed to hold locks while calling
* message delivery.
*/
+
+ rcu_read_lock();
+
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (intf->curr_msg == NULL && !intf->in_shutdown) {
@@ -3900,6 +3903,8 @@ static void smi_recv_tasklet(unsigned long val)
if (newmsg)
intf->handlers->sender(intf->send_info, newmsg);
+ rcu_read_unlock();
+
handle_new_recv_msgs(intf);
}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 59ee93ea84eb..985973855005 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -61,6 +61,7 @@
#include <linux/ipmi_smi.h>
#include <asm/io.h>
#include "ipmi_si_sm.h"
+#include "ipmi_dmi.h"
#include <linux/dmi.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -1942,7 +1943,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
info->io.regspacing = DEFAULT_REGSPACING;
info->io.regsize = regsize;
if (!info->io.regsize)
- info->io.regsize = DEFAULT_REGSPACING;
+ info->io.regsize = DEFAULT_REGSIZE;
info->io.regshift = regshift;
info->irq = irq;
if (info->irq)
@@ -2036,7 +2037,7 @@ static int hardcode_find_bmc(void)
info->io.regspacing = DEFAULT_REGSPACING;
info->io.regsize = regsizes[i];
if (!info->io.regsize)
- info->io.regsize = DEFAULT_REGSPACING;
+ info->io.regsize = DEFAULT_REGSIZE;
info->io.regshift = regshifts[i];
info->irq = irqs[i];
if (info->irq)
@@ -2273,136 +2274,105 @@ static void spmi_find_bmc(void)
}
#endif
-#ifdef CONFIG_DMI
-struct dmi_ipmi_data {
- u8 type;
- u8 addr_space;
- unsigned long base_addr;
- u8 irq;
- u8 offset;
- u8 slave_addr;
-};
-
-static int decode_dmi(const struct dmi_header *dm,
- struct dmi_ipmi_data *dmi)
+#if defined(CONFIG_DMI) || defined(CONFIG_ACPI)
+struct resource *ipmi_get_info_from_resources(struct platform_device *pdev,
+ struct smi_info *info)
{
- const u8 *data = (const u8 *)dm;
- unsigned long base_addr;
- u8 reg_spacing;
- u8 len = dm->length;
-
- dmi->type = data[4];
+ struct resource *res, *res_second;
- memcpy(&base_addr, data+8, sizeof(unsigned long));
- if (len >= 0x11) {
- if (base_addr & 1) {
- /* I/O */
- base_addr &= 0xFFFE;
- dmi->addr_space = IPMI_IO_ADDR_SPACE;
- } else
- /* Memory */
- dmi->addr_space = IPMI_MEM_ADDR_SPACE;
-
- /* If bit 4 of byte 0x10 is set, then the lsb for the address
- is odd. */
- dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4);
-
- dmi->irq = data[0x11];
-
- /* The top two bits of byte 0x10 hold the register spacing. */
- reg_spacing = (data[0x10] & 0xC0) >> 6;
- switch (reg_spacing) {
- case 0x00: /* Byte boundaries */
- dmi->offset = 1;
- break;
- case 0x01: /* 32-bit boundaries */
- dmi->offset = 4;
- break;
- case 0x02: /* 16-byte boundaries */
- dmi->offset = 16;
- break;
- default:
- /* Some other interface, just ignore it. */
- return -EIO;
- }
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res) {
+ info->io_setup = port_setup;
+ info->io.addr_type = IPMI_IO_ADDR_SPACE;
} else {
- /* Old DMI spec. */
- /*
- * Note that technically, the lower bit of the base
- * address should be 1 if the address is I/O and 0 if
- * the address is in memory. So many systems get that
- * wrong (and all that I have seen are I/O) so we just
- * ignore that bit and assume I/O. Systems that use
- * memory should use the newer spec, anyway.
- */
- dmi->base_addr = base_addr & 0xfffe;
- dmi->addr_space = IPMI_IO_ADDR_SPACE;
- dmi->offset = 1;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ info->io_setup = mem_setup;
+ info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+ }
}
+ if (!res) {
+ dev_err(&pdev->dev, "no I/O or memory address\n");
+ return NULL;
+ }
+ info->io.addr_data = res->start;
- dmi->slave_addr = data[6];
+ info->io.regspacing = DEFAULT_REGSPACING;
+ res_second = platform_get_resource(pdev,
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ?
+ IORESOURCE_IO : IORESOURCE_MEM,
+ 1);
+ if (res_second) {
+ if (res_second->start > info->io.addr_data)
+ info->io.regspacing =
+ res_second->start - info->io.addr_data;
+ }
+ info->io.regsize = DEFAULT_REGSIZE;
+ info->io.regshift = 0;
- return 0;
+ return res;
}
-static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
+#endif
+
+#ifdef CONFIG_DMI
+static int dmi_ipmi_probe(struct platform_device *pdev)
{
struct smi_info *info;
+ u8 type, slave_addr;
+ int rv;
+
+ if (!si_trydmi)
+ return -ENODEV;
+
+ rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
+ if (rv)
+ return -ENODEV;
info = smi_info_alloc();
if (!info) {
pr_err(PFX "Could not allocate SI data\n");
- return;
+ return -ENOMEM;
}
info->addr_source = SI_SMBIOS;
pr_info(PFX "probing via SMBIOS\n");
- switch (ipmi_data->type) {
- case 0x01: /* KCS */
+ switch (type) {
+ case IPMI_DMI_TYPE_KCS:
info->si_type = SI_KCS;
break;
- case 0x02: /* SMIC */
+ case IPMI_DMI_TYPE_SMIC:
info->si_type = SI_SMIC;
break;
- case 0x03: /* BT */
+ case IPMI_DMI_TYPE_BT:
info->si_type = SI_BT;
break;
default:
kfree(info);
- return;
+ return -EINVAL;
}
- switch (ipmi_data->addr_space) {
- case IPMI_MEM_ADDR_SPACE:
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- break;
-
- case IPMI_IO_ADDR_SPACE:
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- break;
-
- default:
- kfree(info);
- pr_warn(PFX "Unknown SMBIOS I/O Address type: %d\n",
- ipmi_data->addr_space);
- return;
+ if (!ipmi_get_info_from_resources(pdev, info)) {
+ rv = -EINVAL;
+ goto err_free;
}
- info->io.addr_data = ipmi_data->base_addr;
- info->io.regspacing = ipmi_data->offset;
- if (!info->io.regspacing)
- info->io.regspacing = DEFAULT_REGSPACING;
- info->io.regsize = DEFAULT_REGSPACING;
- info->io.regshift = 0;
-
- info->slave_addr = ipmi_data->slave_addr;
+ rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
+ if (rv) {
+ dev_warn(&pdev->dev, "device has no slave-addr property");
+ info->slave_addr = 0x20;
+ } else {
+ info->slave_addr = slave_addr;
+ }
- info->irq = ipmi_data->irq;
- if (info->irq)
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq > 0)
info->irq_setup = std_irq_setup;
+ else
+ info->irq = 0;
+
+ info->dev = &pdev->dev;
pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
(info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
@@ -2411,21 +2381,17 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
if (add_smi(info))
kfree(info);
-}
-static void dmi_find_bmc(void)
-{
- const struct dmi_device *dev = NULL;
- struct dmi_ipmi_data data;
- int rv;
+ return 0;
- while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) {
- memset(&data, 0, sizeof(data));
- rv = decode_dmi((const struct dmi_header *) dev->device_data,
- &data);
- if (!rv)
- try_init_dmi(&data);
- }
+err_free:
+ kfree(info);
+ return rv;
+}
+#else
+static int dmi_ipmi_probe(struct platform_device *pdev)
+{
+ return -ENODEV;
}
#endif /* CONFIG_DMI */
@@ -2684,17 +2650,47 @@ static int of_ipmi_probe(struct platform_device *dev)
#endif
#ifdef CONFIG_ACPI
+static int find_slave_address(struct smi_info *info, int slave_addr)
+{
+#ifdef CONFIG_IPMI_DMI_DECODE
+ if (!slave_addr) {
+ int type = -1;
+ u32 flags = IORESOURCE_IO;
+
+ switch (info->si_type) {
+ case SI_KCS:
+ type = IPMI_DMI_TYPE_KCS;
+ break;
+ case SI_BT:
+ type = IPMI_DMI_TYPE_BT;
+ break;
+ case SI_SMIC:
+ type = IPMI_DMI_TYPE_SMIC;
+ break;
+ }
+
+ if (info->io.addr_type == IPMI_MEM_ADDR_SPACE)
+ flags = IORESOURCE_MEM;
+
+ slave_addr = ipmi_dmi_get_slave_addr(type, flags,
+ info->io.addr_data);
+ }
+#endif
+
+ return slave_addr;
+}
+
static int acpi_ipmi_probe(struct platform_device *dev)
{
struct smi_info *info;
- struct resource *res, *res_second;
acpi_handle handle;
acpi_status status;
unsigned long long tmp;
+ struct resource *res;
int rv = -EINVAL;
if (!si_tryacpi)
- return 0;
+ return -ENODEV;
handle = ACPI_HANDLE(&dev->dev);
if (!handle)
@@ -2734,35 +2730,11 @@ static int acpi_ipmi_probe(struct platform_device *dev)
goto err_free;
}
- res = platform_get_resource(dev, IORESOURCE_IO, 0);
- if (res) {
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (res) {
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- }
- }
+ res = ipmi_get_info_from_resources(dev, info);
if (!res) {
- dev_err(&dev->dev, "no I/O or memory address\n");
+ rv = -EINVAL;
goto err_free;
}
- info->io.addr_data = res->start;
-
- info->io.regspacing = DEFAULT_REGSPACING;
- res_second = platform_get_resource(dev,
- (info->io.addr_type == IPMI_IO_ADDR_SPACE) ?
- IORESOURCE_IO : IORESOURCE_MEM,
- 1);
- if (res_second) {
- if (res_second->start > info->io.addr_data)
- info->io.regspacing =
- res_second->start - info->io.addr_data;
- }
- info->io.regsize = DEFAULT_REGSPACING;
- info->io.regshift = 0;
/* If _GPE exists, use it; otherwise use standard interrupts */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
@@ -2778,6 +2750,8 @@ static int acpi_ipmi_probe(struct platform_device *dev)
}
}
+ info->slave_addr = find_slave_address(info, info->slave_addr);
+
info->dev = &dev->dev;
platform_set_drvdata(dev, info);
@@ -2813,7 +2787,10 @@ static int ipmi_probe(struct platform_device *dev)
if (of_ipmi_probe(dev) == 0)
return 0;
- return acpi_ipmi_probe(dev);
+ if (acpi_ipmi_probe(dev) == 0)
+ return 0;
+
+ return dmi_ipmi_probe(dev);
}
static int ipmi_remove(struct platform_device *dev)
@@ -3786,11 +3763,6 @@ static int init_ipmi_si(void)
}
#endif
-#ifdef CONFIG_DMI
- if (si_trydmi)
- dmi_find_bmc();
-#endif
-
#ifdef CONFIG_ACPI
if (si_tryacpi)
spmi_find_bmc();
@@ -3938,6 +3910,7 @@ static void cleanup_ipmi_si(void)
}
module_exit(cleanup_ipmi_si);
+MODULE_ALIAS("platform:dmi-ipmi-si");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
MODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT"
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 0b22a9be5029..0aea3bcb6158 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -53,6 +53,7 @@
#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/time64.h>
+#include "ipmi_dmi.h"
#define PFX "ipmi_ssif: "
#define DEVICE_NAME "ipmi_ssif"
@@ -180,6 +181,8 @@ struct ssif_addr_info {
int slave_addr;
enum ipmi_addr_src addr_src;
union ipmi_smi_info_union addr_info;
+ struct device *dev;
+ struct i2c_client *client;
struct mutex clients_mutex;
struct list_head clients;
@@ -408,6 +411,7 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags)
msg = ipmi_alloc_smi_msg();
if (!msg) {
ssif_info->ssif_state = SSIF_NORMAL;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -430,6 +434,7 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info,
msg = ipmi_alloc_smi_msg();
if (!msg) {
ssif_info->ssif_state = SSIF_NORMAL;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -761,6 +766,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
result, len, data[2]);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
+ /*
+ * Don't abort here, maybe it was a queued
+ * response to a previous command.
+ */
+ ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn(PFX "Invalid response getting flags: %x %x\n",
data[0], data[1]);
} else {
@@ -1094,7 +1104,7 @@ static int inc_usecount(void *send_info)
{
struct ssif_info *ssif_info = send_info;
- if (!i2c_get_adapter(ssif_info->client->adapter->nr))
+ if (!i2c_get_adapter(i2c_adapter_id(ssif_info->client->adapter)))
return -ENODEV;
i2c_use_client(ssif_info->client);
@@ -1169,6 +1179,7 @@ static LIST_HEAD(ssif_infos);
static int ssif_remove(struct i2c_client *client)
{
struct ssif_info *ssif_info = i2c_get_clientdata(client);
+ struct ssif_addr_info *addr_info;
int rv;
if (!ssif_info)
@@ -1196,6 +1207,13 @@ static int ssif_remove(struct i2c_client *client)
kthread_stop(ssif_info->thread);
}
+ list_for_each_entry(addr_info, &ssif_infos, link) {
+ if (addr_info->client == client) {
+ addr_info->client = NULL;
+ break;
+ }
+ }
+
/*
* No message can be outstanding now, we have removed the
* upper layer and it permitted us to do so.
@@ -1404,28 +1422,13 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev)
static int find_slave_address(struct i2c_client *client, int slave_addr)
{
- struct ssif_addr_info *info;
-
- if (slave_addr)
- return slave_addr;
-
- /*
- * Came in without a slave address, search around to see if
- * the other sources have a slave address. This lets us pick
- * up an SMBIOS slave address when using ACPI.
- */
- list_for_each_entry(info, &ssif_infos, link) {
- if (info->binfo.addr != client->addr)
- continue;
- if (info->adapter_name && client->adapter->name &&
- strcmp_nospace(info->adapter_name,
- client->adapter->name))
- continue;
- if (info->slave_addr) {
- slave_addr = info->slave_addr;
- break;
- }
- }
+#ifdef CONFIG_IPMI_DMI_DECODE
+ if (!slave_addr)
+ slave_addr = ipmi_dmi_get_slave_addr(
+ IPMI_DMI_TYPE_SSIF,
+ i2c_adapter_id(client->adapter),
+ client->addr);
+#endif
return slave_addr;
}
@@ -1447,7 +1450,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
u8 slave_addr = 0;
struct ssif_addr_info *addr_info = NULL;
-
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp)
return -ENOMEM;
@@ -1468,6 +1470,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info->addr_source = addr_info->addr_src;
ssif_info->ssif_debug = addr_info->debug;
ssif_info->addr_info = addr_info->addr_info;
+ addr_info->client = client;
slave_addr = addr_info->slave_addr;
}
}
@@ -1664,7 +1667,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
unsigned int thread_num;
- thread_num = ((ssif_info->client->adapter->nr << 8) |
+ thread_num = ((i2c_adapter_id(ssif_info->client->adapter)
+ << 8) |
ssif_info->client->addr);
init_completion(&ssif_info->wake_thread);
ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
@@ -1705,8 +1709,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
out:
- if (rv)
+ if (rv) {
+ /*
+ * Note that if addr_info->client is assigned, we
+ * leave it. The i2c client hangs around even if we
+ * return a failure here, and the failure here is not
+ * propagated back to the i2c code. This seems to be
+ * design intent, strange as it may be. But if we
+ * don't leave it, ssif_platform_remove will not remove
+ * the client like it should.
+ */
+ dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv);
kfree(ssif_info);
+ }
kfree(resp);
return rv;
@@ -1731,7 +1746,8 @@ static int ssif_adapter_handler(struct device *adev, void *opaque)
static int new_ssif_client(int addr, char *adapter_name,
int debug, int slave_addr,
- enum ipmi_addr_src addr_src)
+ enum ipmi_addr_src addr_src,
+ struct device *dev)
{
struct ssif_addr_info *addr_info;
int rv = 0;
@@ -1764,6 +1780,10 @@ static int new_ssif_client(int addr, char *adapter_name,
addr_info->debug = debug;
addr_info->slave_addr = slave_addr;
addr_info->addr_src = addr_src;
+ addr_info->dev = dev;
+
+ if (dev)
+ dev_set_drvdata(dev, addr_info);
list_add_tail(&addr_info->link, &ssif_infos);
@@ -1902,7 +1922,7 @@ static int try_init_spmi(struct SPMITable *spmi)
myaddr = spmi->addr.address & 0x7f;
- return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI);
+ return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI, NULL);
}
static void spmi_find_bmc(void)
@@ -1931,48 +1951,40 @@ static void spmi_find_bmc(void) { }
#endif
#ifdef CONFIG_DMI
-static int decode_dmi(const struct dmi_device *dmi_dev)
+static int dmi_ipmi_probe(struct platform_device *pdev)
{
- struct dmi_header *dm = dmi_dev->device_data;
- u8 *data = (u8 *) dm;
- u8 len = dm->length;
- unsigned short myaddr;
- int slave_addr;
+ u8 type, slave_addr = 0;
+ u16 i2c_addr;
+ int rv;
- if (num_addrs >= MAX_SSIF_BMCS)
- return -1;
+ if (!ssif_trydmi)
+ return -ENODEV;
- if (len < 9)
- return -1;
+ rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
+ if (rv)
+ return -ENODEV;
- if (data[0x04] != 4) /* Not SSIF */
- return -1;
+ if (type != IPMI_DMI_TYPE_SSIF)
+ return -ENODEV;
- if ((data[8] >> 1) == 0) {
- /*
- * Some broken systems put the I2C address in
- * the slave address field. We try to
- * accommodate them here.
- */
- myaddr = data[6] >> 1;
- slave_addr = 0;
- } else {
- myaddr = data[8] >> 1;
- slave_addr = data[6];
+ rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr);
+ if (rv) {
+ dev_warn(&pdev->dev, PFX "No i2c-addr property\n");
+ return -ENODEV;
}
- return new_ssif_client(myaddr, NULL, 0, slave_addr, SI_SMBIOS);
-}
-
-static void dmi_iterator(void)
-{
- const struct dmi_device *dev = NULL;
+ rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
+ if (rv)
+ dev_warn(&pdev->dev, "device has no slave-addr property");
- while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
- decode_dmi(dev);
+ return new_ssif_client(i2c_addr, NULL, 0,
+ slave_addr, SI_SMBIOS, &pdev->dev);
}
#else
-static void dmi_iterator(void) { }
+static int dmi_ipmi_probe(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
#endif
static const struct i2c_device_id ssif_id[] = {
@@ -1993,6 +2005,36 @@ static struct i2c_driver ssif_i2c_driver = {
.detect = ssif_detect
};
+static int ssif_platform_probe(struct platform_device *dev)
+{
+ return dmi_ipmi_probe(dev);
+}
+
+static int ssif_platform_remove(struct platform_device *dev)
+{
+ struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev);
+
+ if (!addr_info)
+ return 0;
+
+ mutex_lock(&ssif_infos_mutex);
+ if (addr_info->client)
+ i2c_unregister_device(addr_info->client);
+
+ list_del(&addr_info->link);
+ kfree(addr_info);
+ mutex_unlock(&ssif_infos_mutex);
+ return 0;
+}
+
+static struct platform_driver ipmi_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+ .probe = ssif_platform_probe,
+ .remove = ssif_platform_remove,
+};
+
static int init_ipmi_ssif(void)
{
int i;
@@ -2007,7 +2049,7 @@ static int init_ipmi_ssif(void)
for (i = 0; i < num_addrs; i++) {
rv = new_ssif_client(addr[i], adapter_name[i],
dbg[i], slave_addrs[i],
- SI_HARDCODED);
+ SI_HARDCODED, NULL);
if (rv)
pr_err(PFX
"Couldn't add hardcoded device at addr 0x%x\n",
@@ -2017,11 +2059,16 @@ static int init_ipmi_ssif(void)
if (ssif_tryacpi)
ssif_i2c_driver.driver.acpi_match_table =
ACPI_PTR(ssif_acpi_match);
- if (ssif_trydmi)
- dmi_iterator();
+
if (ssif_tryacpi)
spmi_find_bmc();
+ if (ssif_trydmi) {
+ rv = platform_driver_register(&ipmi_driver);
+ if (rv)
+ pr_err(PFX "Unable to register driver: %d\n", rv);
+ }
+
ssif_i2c_driver.address_list = ssif_address_list();
rv = i2c_add_driver(&ssif_i2c_driver);
@@ -2041,10 +2088,13 @@ static void cleanup_ipmi_ssif(void)
i2c_del_driver(&ssif_i2c_driver);
+ platform_driver_unregister(&ipmi_driver);
+
free_ssif_clients();
}
module_exit(cleanup_ipmi_ssif);
+MODULE_ALIAS("platform:dmi-ipmi-ssif");
MODULE_AUTHOR("Todd C Davis <todd.c.davis@intel.com>, Corey Minyard <minyard@acm.org>");
MODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index a5c6cfe71a8e..3d832d0362a4 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1163,10 +1163,11 @@ static int wdog_reboot_handler(struct notifier_block *this,
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
} else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
- /* Set a long timer to let the reboot happens, but
- reboot if it hangs, but only if the watchdog
+ /* Set a long timer to let the reboot happen or
+ reset if it hangs, but only if the watchdog
timer was already running. */
- timeout = 120;
+ if (timeout < 120)
+ timeout = 120;
pretimeout = 0;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 01a260f67437..afa3ce7d3e72 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -288,7 +288,6 @@
#define SEC_XFER_SIZE 512
#define EXTRACT_SIZE 10
-#define DEBUG_RANDOM_BOOT 0
#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
@@ -437,6 +436,7 @@ static void _extract_crng(struct crng_state *crng,
static void _crng_backtrack_protect(struct crng_state *crng,
__u8 tmp[CHACHA20_BLOCK_SIZE], int used);
static void process_random_ready_list(void);
+static void _get_random_bytes(void *buf, int nbytes);
/**********************************************************************
*
@@ -777,7 +777,7 @@ static void crng_initialize(struct crng_state *crng)
_extract_entropy(&input_pool, &crng->state[4],
sizeof(__u32) * 12, 0);
else
- get_random_bytes(&crng->state[4], sizeof(__u32) * 12);
+ _get_random_bytes(&crng->state[4], sizeof(__u32) * 12);
for (i = 4; i < 16; i++) {
if (!arch_get_random_seed_long(&rv) &&
!arch_get_random_long(&rv))
@@ -851,11 +851,6 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
}
}
-static inline void crng_wait_ready(void)
-{
- wait_event_interruptible(crng_init_wait, crng_ready());
-}
-
static void _extract_crng(struct crng_state *crng,
__u8 out[CHACHA20_BLOCK_SIZE])
{
@@ -987,6 +982,11 @@ void add_device_randomness(const void *buf, unsigned int size)
unsigned long time = random_get_entropy() ^ jiffies;
unsigned long flags;
+ if (!crng_ready()) {
+ crng_fast_load(buf, size);
+ return;
+ }
+
trace_add_device_randomness(size, _RET_IP_);
spin_lock_irqsave(&input_pool.lock, flags);
_mix_pool_bytes(&input_pool, buf, size);
@@ -1472,22 +1472,44 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
return ret;
}
+#define warn_unseeded_randomness(previous) \
+ _warn_unseeded_randomness(__func__, (void *) _RET_IP_, (previous))
+
+static void _warn_unseeded_randomness(const char *func_name, void *caller,
+ void **previous)
+{
+#ifdef CONFIG_WARN_ALL_UNSEEDED_RANDOM
+ const bool print_once = false;
+#else
+ static bool print_once __read_mostly;
+#endif
+
+ if (print_once ||
+ crng_ready() ||
+ (previous && (caller == READ_ONCE(*previous))))
+ return;
+ WRITE_ONCE(*previous, caller);
+#ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM
+ print_once = true;
+#endif
+ pr_notice("random: %s called from %pF with crng_init=%d\n",
+ func_name, caller, crng_init);
+}
+
/*
* This function is the exported kernel interface. It returns some
* number of good random numbers, suitable for key generation, seeding
* TCP sequence numbers, etc. It does not rely on the hardware random
* number generator. For random bytes direct from the hardware RNG
- * (when available), use get_random_bytes_arch().
+ * (when available), use get_random_bytes_arch(). In order to ensure
+ * that the randomness provided by this function is okay, the function
+ * wait_for_random_bytes() should be called and return 0 at least once
+ * at any point prior.
*/
-void get_random_bytes(void *buf, int nbytes)
+static void _get_random_bytes(void *buf, int nbytes)
{
__u8 tmp[CHACHA20_BLOCK_SIZE];
-#if DEBUG_RANDOM_BOOT > 0
- if (!crng_ready())
- printk(KERN_NOTICE "random: %pF get_random_bytes called "
- "with crng_init = %d\n", (void *) _RET_IP_, crng_init);
-#endif
trace_get_random_bytes(nbytes, _RET_IP_);
while (nbytes >= CHACHA20_BLOCK_SIZE) {
@@ -1504,9 +1526,35 @@ void get_random_bytes(void *buf, int nbytes)
crng_backtrack_protect(tmp, CHACHA20_BLOCK_SIZE);
memzero_explicit(tmp, sizeof(tmp));
}
+
+void get_random_bytes(void *buf, int nbytes)
+{
+ static void *previous;
+
+ warn_unseeded_randomness(&previous);
+ _get_random_bytes(buf, nbytes);
+}
EXPORT_SYMBOL(get_random_bytes);
/*
+ * Wait for the urandom pool to be seeded and thus guaranteed to supply
+ * cryptographically secure random numbers. This applies to: the /dev/urandom
+ * device, the get_random_bytes function, and the get_random_{u32,u64,int,long}
+ * family of functions. Using any of these functions without first calling
+ * this function forfeits the guarantee of security.
+ *
+ * Returns: 0 if the urandom pool has been seeded.
+ * -ERESTARTSYS if the function was interrupted by a signal.
+ */
+int wait_for_random_bytes(void)
+{
+ if (likely(crng_ready()))
+ return 0;
+ return wait_event_interruptible(crng_init_wait, crng_ready());
+}
+EXPORT_SYMBOL(wait_for_random_bytes);
+
+/*
* Add a callback function that will be invoked when the nonblocking
* pool is initialised.
*
@@ -1860,6 +1908,8 @@ const struct file_operations urandom_fops = {
SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
unsigned int, flags)
{
+ int ret;
+
if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
return -EINVAL;
@@ -1872,9 +1922,9 @@ SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
if (!crng_ready()) {
if (flags & GRND_NONBLOCK)
return -EAGAIN;
- crng_wait_ready();
- if (signal_pending(current))
- return -ERESTARTSYS;
+ ret = wait_for_random_bytes();
+ if (unlikely(ret))
+ return ret;
}
return urandom_read(NULL, buf, count, NULL);
}
@@ -2035,15 +2085,19 @@ static rwlock_t batched_entropy_reset_lock = __RW_LOCK_UNLOCKED(batched_entropy_
/*
* Get a random word for internal kernel use only. The quality of the random
* number is either as good as RDRAND or as good as /dev/urandom, with the
- * goal of being quite fast and not depleting entropy.
+ * goal of being quite fast and not depleting entropy. In order to ensure
+ * that the randomness provided by this function is okay, the function
+ * wait_for_random_bytes() should be called and return 0 at least once
+ * at any point prior.
*/
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u64);
u64 get_random_u64(void)
{
u64 ret;
- bool use_lock = READ_ONCE(crng_init) < 2;
+ bool use_lock;
unsigned long flags = 0;
struct batched_entropy *batch;
+ static void *previous;
#if BITS_PER_LONG == 64
if (arch_get_random_long((unsigned long *)&ret))
@@ -2054,6 +2108,9 @@ u64 get_random_u64(void)
return ret;
#endif
+ warn_unseeded_randomness(&previous);
+
+ use_lock = READ_ONCE(crng_init) < 2;
batch = &get_cpu_var(batched_entropy_u64);
if (use_lock)
read_lock_irqsave(&batched_entropy_reset_lock, flags);
@@ -2073,13 +2130,17 @@ static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u32);
u32 get_random_u32(void)
{
u32 ret;
- bool use_lock = READ_ONCE(crng_init) < 2;
+ bool use_lock;
unsigned long flags = 0;
struct batched_entropy *batch;
+ static void *previous;
if (arch_get_random_int(&ret))
return ret;
+ warn_unseeded_randomness(&previous);
+
+ use_lock = READ_ONCE(crng_init) < 2;
batch = &get_cpu_var(batched_entropy_u32);
if (use_lock)
read_lock_irqsave(&batched_entropy_reset_lock, flags);