From patchwork Fri Jan 29 22:43:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 60845 Delivered-To: patch@linaro.org Received: by 10.112.130.2 with SMTP id oa2csp1416246lbb; Fri, 29 Jan 2016 15:44:52 -0800 (PST) X-Received: by 10.67.2.10 with SMTP id bk10mr17189731pad.26.1454111092462; Fri, 29 Jan 2016 15:44:52 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z2si5083363par.192.2016.01.29.15.44.52; Fri, 29 Jan 2016 15:44:52 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756156AbcA2Xn7 (ORCPT + 30 others); Fri, 29 Jan 2016 18:43:59 -0500 Received: from vms173021pub.verizon.net ([206.46.173.21]:52266 "EHLO vms173021pub.verizon.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755538AbcA2Xn5 (ORCPT ); Fri, 29 Jan 2016 18:43:57 -0500 X-Greylist: delayed 3602 seconds by postgrey-1.27 at vger.kernel.org; Fri, 29 Jan 2016 18:43:57 EST Received: from serve.minyard.net ([173.57.176.17]) by vms173021.mailsrvcs.net (Oracle Communications Messaging Server 7.0.5.32.0 64bit (built Jul 16 2014)) with ESMTPA id <0O1Q00HZ3KGDI380@vms173021.mailsrvcs.net> for linux-kernel@vger.kernel.org; Fri, 29 Jan 2016 16:43:31 -0600 (CST) X-CMAE-Score: 0 X-CMAE-Analysis: v=2.1 cv=EdU1O6SC c=1 sm=1 tr=0 a=bXmWQgKa9n63w7XTPFb8JQ==:117 a=7aQ_Q-yQQ-AA:10 a=fk1lIlRQAAAA:8 a=VwQbUJbxAAAA:8 a=2z0XlXMmxxlb7RQYXlYA:9 a=St72lYBUCNqVKdme:21 a=olX4wOy_-WCT44L-:21 Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id F3E4531FC; Fri, 29 Jan 2016 16:43:24 -0600 (CST) Received: by t430.minyard.net (Postfix, from userid 1000) id 7FA2F30072C; Fri, 29 Jan 2016 16:43:23 -0600 (CST) From: minyard@acm.org To: openipmi-developer@lists.sourceforge.net, linux-kernel@vger.kernel.org Cc: Jean Delvare , Andy Lutomirski , Corey Minyard Subject: [PATCH 3/4] dmi: Move IPMI DMI scanning to the DMI code Date: Fri, 29 Jan 2016 16:43:13 -0600 Message-id: <1454107394-8914-4-git-send-email-minyard@acm.org> X-Mailer: git-send-email 2.5.0 In-reply-to: <1454107394-8914-1-git-send-email-minyard@acm.org> References: <1454107394-8914-1-git-send-email-minyard@acm.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Corey Minyard 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 Cc: Jean Delvare Cc: Andy Lutomirski --- 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(-) -- 2.5.0 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; }