[PATCH 3/4] dmi: Move IPMI DMI scanning to the DMI code

From: minyard
Date: Fri Jan 29 2016 - 18:44:53 EST


From: Corey Minyard <cminyard@xxxxxxxxxx>

It makes more sense to be there, and it's cleaner with the
upcoming conversion of IPMI DMI to a platform device.

Signed-off-by: Corey Minyard <cminyard@xxxxxxxxxx>
Cc: Jean Delvare <jdelvare@xxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
---
drivers/char/ipmi/ipmi_si_intf.c | 119 +++++++--------------------------------
drivers/char/ipmi/ipmi_ssif.c | 40 +++++--------
drivers/firmware/dmi_scan.c | 108 +++++++++++++++++++++++++++++++++--
include/linux/dmi.h | 26 +++++++++
4 files changed, 162 insertions(+), 131 deletions(-)

diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 9fda22e..22292e1 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -2272,98 +2272,36 @@ 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)
+static void try_init_dmi(struct dmi_device *dmi_dev)
{
- const u8 *data = (const u8 *)dm;
- unsigned long base_addr;
- u8 reg_spacing;
- u8 len = dm->length;
+ struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev);
+ struct smi_info *info;

- dmi->type = data[4];
+ if (!ipmi_data)
+ return;

- 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;
- }
- } 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;
+ if (!ipmi_data->good_data) {
+ pr_err(PFX "DMI data for this device was invalid.\n");
+ return;
}

- dmi->slave_addr = data[6];
-
- return 0;
-}
-
-static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
-{
- struct smi_info *info;
-
info = smi_info_alloc();
if (!info) {
- printk(KERN_ERR PFX "Could not allocate SI data\n");
+ pr_err(PFX "Could not allocate SI data\n");
return;
}

info->addr_source = SI_SMBIOS;
- printk(KERN_INFO PFX "probing via SMBIOS\n");
+ pr_info(PFX "probing via SMBIOS\n");

switch (ipmi_data->type) {
- case 0x01: /* KCS */
+ 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:
@@ -2371,22 +2309,12 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
return;
}

- 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:
+ if (ipmi_data->is_io_space) {
info->io_setup = port_setup;
info->io.addr_type = IPMI_IO_ADDR_SPACE;
- break;
-
- default:
- kfree(info);
- printk(KERN_WARNING PFX "Unknown SMBIOS I/O Address type: %d\n",
- ipmi_data->addr_space);
- return;
+ } else {
+ info->io_setup = mem_setup;
+ info->io.addr_type = IPMI_MEM_ADDR_SPACE;
}
info->io.addr_data = ipmi_data->base_addr;

@@ -2413,17 +2341,10 @@ static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)

static void dmi_find_bmc(void)
{
- const struct dmi_device *dev = NULL;
- struct dmi_ipmi_data data;
- int rv;
+ struct dmi_device *dev = NULL;

- 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);
- }
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
+ try_init_dmi(dev);
}
#endif /* CONFIG_DMI */

diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 5f1c3d0..7be0109 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1906,42 +1906,28 @@ static void spmi_find_bmc(void) { }
#endif

#ifdef CONFIG_DMI
-static int decode_dmi(const struct dmi_device *dmi_dev)
+static int decode_dmi(struct dmi_device *dmi_dev)
{
- struct dmi_header *dm = dmi_dev->device_data;
- u8 *data = (u8 *) dm;
- u8 len = dm->length;
- unsigned short myaddr;
- int slave_addr;
-
- if (num_addrs >= MAX_SSIF_BMCS)
- return -1;
+ struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev);

- if (len < 9)
- return -1;
-
- if (data[0x04] != 4) /* Not SSIF */
- return -1;
+ if (!ipmi_data)
+ 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];
+ if (!ipmi_data->good_data) {
+ pr_err(PFX "DMI data for this device was invalid.\n");
+ return -EINVAL;
}

- return new_ssif_client(myaddr, NULL, 0, 0, SI_SMBIOS);
+ if (ipmi_data->type != IPMI_DMI_TYPE_SSIF)
+ return -ENODEV;
+
+ return new_ssif_client(ipmi_data->base_addr, NULL, 0,
+ ipmi_data->slave_addr, SI_SMBIOS);
}

static void dmi_iterator(void)
{
- const struct dmi_device *dev = NULL;
+ struct dmi_device *dev = NULL;

while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
decode_dmi(dev);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 13d9bca..6e4859d 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -313,9 +313,105 @@ static void __init dmi_save_oem_strings_devices(const struct dmi_header *dm)
}
}

+#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(struct dmi_dev_ipmi *dev,
+ const struct dmi_header *dm)
+{
+ const u8 *data = (const u8 *) dm;
+ unsigned long base_addr;
+ u8 len = dm->length;
+ bool slave_addr_set;
+
+ if (len < DMI_IPMI_MIN_LENGTH)
+ return;
+
+ dev->type = data[DMI_IPMI_TYPE];
+
+ memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long));
+ if (len >= DMI_IPMI_VER2_LENGTH) {
+ if (dev->type == IPMI_DMI_TYPE_SSIF) {
+ if ((data[DMI_IPMI_ADDR] >> 1) == 0) {
+ /*
+ * Some broken systems put the I2C address in
+ * the slave address field. We try to
+ * accommodate them here.
+ */
+ dev->base_addr = data[DMI_IPMI_SLAVEADDR] >> 1;
+ dev->slave_addr = 0;
+ slave_addr_set = true;
+ } else {
+ dev->base_addr = data[DMI_IPMI_ADDR] >> 1;
+ }
+ } else {
+ if (base_addr & 1) {
+ /* I/O */
+ base_addr &= DMI_IPMI_IO_MASK;
+ dev->is_io_space = 1;
+ } else {
+ /* Memory */
+ dev->is_io_space = 0;
+ }
+
+ /*
+ * If bit 4 of byte 0x10 is set, then the lsb
+ * for the address is odd.
+ */
+ base_addr |= (data[DMI_IPMI_ACCESS] >> 4) & 1;
+
+ dev->base_addr = base_addr;
+ dev->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 */
+ dev->offset = 1;
+ break;
+ case 1: /* 32-bit boundaries */
+ dev->offset = 4;
+ break;
+ case 2: /* 16-byte boundaries */
+ dev->offset = 16;
+ break;
+ default:
+ /* Leave 0 for undefined. */
+ 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.
+ */
+ dev->base_addr = base_addr & DMI_IPMI_IO_MASK;
+ dev->is_io_space = 1;
+ dev->offset = 1;
+ }
+
+ if (!slave_addr_set)
+ dev->slave_addr = data[DMI_IPMI_SLAVEADDR];
+
+ dev->good_data = 1;
+}
+
static void __init dmi_save_ipmi_device(const struct dmi_header *dm)
{
- struct dmi_device *dev;
+ struct dmi_dev_ipmi *dev;
void *data;

data = dmi_alloc(dm->length);
@@ -328,11 +424,13 @@ static void __init dmi_save_ipmi_device(const struct dmi_header *dm)
if (!dev)
return;

- dev->type = DMI_DEV_TYPE_IPMI;
- dev->name = "IPMI controller";
- dev->device_data = data;
+ dev->dev.type = DMI_DEV_TYPE_IPMI;
+ dev->dev.name = "IPMI controller";
+ dev->dev.device_data = data;

- dmi_devices_list_add(dev);
+ dmi_decode_ipmi(dev, dm);
+
+ dmi_devices_list_add(&dev->dev);
}

static void __init dmi_save_dev_pciaddr(int instance, int segment, int bus,
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index e130c38..ca37ba5 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -97,6 +97,24 @@ struct dmi_dev_onboard {
int devfn;
};

+/* Device data for an IPMI entry. */
+struct dmi_dev_ipmi {
+ struct dmi_device dev;
+ u8 good_data;
+ u8 type;
+ u8 is_io_space;
+ u8 irq;
+ u8 offset;
+ u8 slave_addr;
+ unsigned long base_addr;
+};
+
+/* dmi_ipmi_data type field */
+#define IPMI_DMI_TYPE_KCS 0x01
+#define IPMI_DMI_TYPE_SMIC 0x02
+#define IPMI_DMI_TYPE_BT 0x03
+#define IPMI_DMI_TYPE_SSIF 0x04
+
extern struct kobject *dmi_kobj;
extern int dmi_check_system(const struct dmi_system_id *list);
const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
@@ -127,6 +145,14 @@ static inline struct dmi_device *to_dmi_device(struct fwnode_handle *fwnode)
return NULL;
}

+static inline struct dmi_dev_ipmi *to_dmi_dev_ipmi(struct dmi_device *dev)
+{
+ if (!dev || dev->type != DMI_DEV_TYPE_IPMI)
+ return NULL;
+
+ return container_of(dev, struct dmi_dev_ipmi, dev);
+}
+
#else

static inline int dmi_check_system(const struct dmi_system_id *list) { return 0; }
--
2.5.0