Updates for the IPMI driver

A new type of low-level IPMI driver is added for direct communication
 over the IPMI message bus without a BMC between the driver and the bus.
 
 Other than that, lots of little bug fixes and enhancements.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmF/5YwACgkQYfOMkJGb
 /4GV5A/8CSXagtu6JJTY88+7CNxXSca4fIGcpO8RZUA1b+Hkw3+y/Qlv/djK6bk1
 lt16OpMxSYggTXtkfH6f8zRnf5jAbXDIrcdYAZdCdIt/u4zgXksa8XycUgYDcVS8
 Tb07qO55HsNwjjjPglIUXqohkpyuilJLpBDhWW+AWNDs2niAwFFpFoBTDnDbZEe1
 6ide1f/IBhibwEx4T4nGs45VKUB+jHsORSj8+fB2suREgs0GTGf51E9NyR91CYyk
 uSkqpeHVN85KzTWAeAV5nCfRab/rBVS/EL8tEQEt1LniXZpJs3bJHLJFhgaQWesB
 6b2hVdL8FoG5qO1Xcf2ofbLKdrY+AOWW8gBWfsZmsK2dQYex+CHO7bm2BkRt037i
 YJdg5+qXz093uDCotYZYvGjm+TTmrTeFVhEJD6+CW0AyVeSAqBB3TBxqDpBn3LfN
 p2sTUS/1BZuChe0VXPXcDkkUFsVs3J+Z4lFl0f/WzTFUxWZEC/ELh/3Nuz7J2eYy
 gEXWqvox8BAU8byYucmLNb7xelFztjXpga+WSDk7kEIIStM6aeIVKenpYI/KJCr/
 jZh0M/zYUk4QVMf54nsWEfWOiKi7D4UyE+C3Ijta2UordLTetLeY40HVZqyPwbPE
 AMDJH2MhmIr5soIw1yMVP0cp+6CWatBgnrHcXSbBc8ZpJEJzCNU=
 =WxcC
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi

Pull IPMI driver updates from Corey Minyard:
 "A new type of low-level IPMI driver is added for direct communication
  over the IPMI message bus without a BMC between the driver and the
  bus.

  Other than that, lots of little bug fixes and enhancements"

* tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi:
  ipmi: kcs_bmc: Fix a memory leak in the error handling path of 'kcs_bmc_serio_add_device()'
  char: ipmi: replace snprintf in show functions with sysfs_emit
  ipmi: ipmb: fix dependencies to eliminate build error
  ipmi:ipmb: Add OF support
  ipmi: bt: Add ast2600 compatible string
  ipmi: bt-bmc: Use registers directly
  ipmi: ipmb: Fix off-by-one size check on rcvlen
  ipmi:ssif: Use depends on, not select, for I2C
  ipmi: Add docs for the IPMI IPMB driver
  ipmi: Add docs for IPMB direct addressing
  ipmi:ipmb: Add initial support for IPMI over IPMB
  ipmi: Add support for IPMB direct messages
  ipmi: Export ipmb_checksum()
  ipmi: Fix a typo
  ipmi: Check error code before processing BMC response
  ipmi:devintf: Return a proper error when recv buffer too small
  ipmi: Disable some operations during a panic
  ipmi:watchdog: Set panic count to proper value on a panic
This commit is contained in:
Linus Torvalds 2021-11-01 18:53:03 -07:00
commit 316b7eaa93
16 changed files with 1078 additions and 123 deletions

View File

@ -9,6 +9,7 @@ Required properties:
- compatible : should be one of
"aspeed,ast2400-ibt-bmc"
"aspeed,ast2500-ibt-bmc"
"aspeed,ast2600-ibt-bmc"
- reg: physical address and size of the registers
Optional properties:

View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/ipmi/ipmi-ipmb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IPMI IPMB device bindings
description: IPMI IPMB device bindings
maintainers:
- Corey Minyard <cminyard@mvista.com>
properties:
compatible:
enum:
- ipmi-ipmb
device_type:
items:
- const: "ipmi"
reg:
maxItems: 1
bmcaddr:
$ref: /schemas/types.yaml#/definitions/uint8
description: The address of the BMC on the IPMB bus. Defaults to 0x20.
retry-time:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Time between retries of sends, in milliseconds. Defaults to 250.
max-retries:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of retries before a failure is declared. Defaults to 1.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
ipmi-ipmb@40 {
compatible = "ipmi-ipmb";
device_type = "ipmi";
reg = <0x40>;
bmcaddr = /bits/ 8 <0x20>;
retry-time = <250>;
max-retries = <1>;
};
};

View File

@ -166,8 +166,8 @@ and the type is IPMI_SYSTEM_INTERFACE_ADDR_TYPE. This is used for talking
straight to the BMC on the current card. The channel must be
IPMI_BMC_CHANNEL.
Messages that are destined to go out on the IPMB bus use the
IPMI_IPMB_ADDR_TYPE address type. The format is::
Messages that are destined to go out on the IPMB bus going through the
BMC use the IPMI_IPMB_ADDR_TYPE address type. The format is::
struct ipmi_ipmb_addr
{
@ -181,6 +181,23 @@ The "channel" here is generally zero, but some devices support more
than one channel, it corresponds to the channel as defined in the IPMI
spec.
There is also an IPMB direct address for a situation where the sender
is directly on an IPMB bus and doesn't have to go through the BMC.
You can send messages to a specific management controller (MC) on the
IPMB using the IPMI_IPMB_DIRECT_ADDR_TYPE with the following format::
struct ipmi_ipmb_direct_addr
{
int addr_type;
short channel;
unsigned char slave_addr;
unsigned char rq_lun;
unsigned char rs_lun;
};
The channel is always zero. You can also receive commands from other
MCs that you have registered to handle and respond to them, so you can
use this to implement a management controller on a bus..
Messages
--------
@ -348,6 +365,10 @@ user may be registered for each netfn/cmd/channel, but different users
may register for different commands, or the same command if the
channel bitmasks do not overlap.
To respond to a received command, set the response bit in the returned
netfn, use the address from the received message, and use the same
msgid that you got in the receive message.
From userland, equivalent IOCTLs are provided to do these functions.
@ -570,6 +591,45 @@ web page.
The driver supports a hot add and remove of interfaces through the I2C
sysfs interface.
The IPMI IPMB Driver
--------------------
This driver is for supporting a system that sits on an IPMB bus; it
allows the interface to look like a normal IPMI interface. Sending
system interface addressed messages to it will cause the message to go
to the registered BMC on the system (default at IPMI address 0x20).
It also allows you to directly address other MCs on the bus using the
ipmb direct addressing. You can receive commands from other MCs on
the bus and they will be handled through the normal received command
mechanism described above.
Parameters are::
ipmi_ipmb.bmcaddr=<address to use for system interface addresses messages>
ipmi_ipmb.retry_time_ms=<Time between retries on IPMB>
ipmi_ipmb.max_retries=<Number of times to retry a message>
Loading the module will not result in the driver automatcially
starting unless there is device tree information setting it up. If
you want to instantiate one of these by hand, do::
echo ipmi-ipmb <addr> > /sys/class/i2c-dev/i2c-<n>/device/new_device
Note that the address you give here is the I2C address, not the IPMI
address. So if you want your MC address to be 0x60, you put 0x30
here. See the I2C driver info for more details.
Command bridging to other IPMB busses through this interface does not
work. The receive message queue is not implemented, by design. There
is only one receive message queue on a BMC, and that is meant for the
host drivers, not something on the IPMB bus.
A BMC may have multiple IPMB busses, which bus your device sits on
depends on how the system is wired. You can fetch the channels with
"ipmitool channel info <n>" where <n> is the channel, with the
channels being 0-7 and try the IPMB channels.
Other Pieces
------------

View File

@ -69,12 +69,21 @@ config IPMI_SI
config IPMI_SSIF
tristate 'IPMI SMBus handler (SSIF)'
select I2C
depends on I2C
help
Provides a driver for a SMBus interface to a BMC, meaning that you
have a driver that must be accessed over an I2C bus instead of a
standard interface. This module requires I2C support.
config IPMI_IPMB
tristate 'IPMI IPMB interface'
depends on I2C && I2C_SLAVE
help
Provides a driver for a system running right on the IPMB bus.
It supports normal system interface messages to a BMC on the IPMB
bus, and it also supports direct messaging on the bus using
IPMB direct messages. This module requires I2C support.
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_IPMB) += ipmi_ipmb.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o

View File

@ -8,13 +8,11 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/timer.h>
@ -59,8 +57,7 @@
struct bt_bmc {
struct device dev;
struct miscdevice miscdev;
struct regmap *map;
int offset;
void __iomem *base;
int irq;
wait_queue_head_t queue;
struct timer_list poll_timer;
@ -69,29 +66,14 @@ struct bt_bmc {
static atomic_t open_count = ATOMIC_INIT(0);
static const struct regmap_config bt_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
{
uint32_t val = 0;
int rc;
rc = regmap_read(bt_bmc->map, bt_bmc->offset + reg, &val);
WARN(rc != 0, "regmap_read() failed: %d\n", rc);
return rc == 0 ? (u8) val : 0;
return readb(bt_bmc->base + reg);
}
static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg)
{
int rc;
rc = regmap_write(bt_bmc->map, bt_bmc->offset + reg, data);
WARN(rc != 0, "regmap_write() failed: %d\n", rc);
writeb(data, bt_bmc->base + reg);
}
static void clr_rd_ptr(struct bt_bmc *bt_bmc)
@ -376,18 +358,15 @@ static irqreturn_t bt_bmc_irq(int irq, void *arg)
{
struct bt_bmc *bt_bmc = arg;
u32 reg;
int rc;
rc = regmap_read(bt_bmc->map, bt_bmc->offset + BT_CR2, &reg);
if (rc)
return IRQ_NONE;
reg = readl(bt_bmc->base + BT_CR2);
reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY;
if (!reg)
return IRQ_NONE;
/* ack pending IRQs */
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR2, reg);
writel(reg, bt_bmc->base + BT_CR2);
wake_up(&bt_bmc->queue);
return IRQ_HANDLED;
@ -398,6 +377,7 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
{
struct device *dev = &pdev->dev;
int rc;
u32 reg;
bt_bmc->irq = platform_get_irq_optional(pdev, 0);
if (bt_bmc->irq < 0)
@ -417,11 +397,11 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
* will be cleared (along with B2H) when we can write the next
* message to the BT buffer
*/
rc = regmap_update_bits(bt_bmc->map, bt_bmc->offset + BT_CR1,
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY),
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY));
reg = readl(bt_bmc->base + BT_CR1);
reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY;
writel(reg, bt_bmc->base + BT_CR1);
return rc;
return 0;
}
static int bt_bmc_probe(struct platform_device *pdev)
@ -439,25 +419,9 @@ static int bt_bmc_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, bt_bmc);
bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(bt_bmc->map)) {
void __iomem *base;
/*
* Assume it's not the MFD-based devicetree description, in
* which case generate a regmap ourselves
*/
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
bt_bmc->map = devm_regmap_init_mmio(dev, base, &bt_regmap_cfg);
bt_bmc->offset = 0;
} else {
rc = of_property_read_u32(dev->of_node, "reg", &bt_bmc->offset);
if (rc)
return rc;
}
bt_bmc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bt_bmc->base))
return PTR_ERR(bt_bmc->base);
mutex_init(&bt_bmc->mutex);
init_waitqueue_head(&bt_bmc->queue);
@ -483,12 +447,12 @@ static int bt_bmc_probe(struct platform_device *pdev)
add_timer(&bt_bmc->poll_timer);
}
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR0,
(BT_IO_BASE << BT_CR0_IO_BASE) |
writel((BT_IO_BASE << BT_CR0_IO_BASE) |
(BT_IRQ << BT_CR0_IRQ) |
BT_CR0_EN_CLR_SLV_RDP |
BT_CR0_EN_CLR_SLV_WRP |
BT_CR0_ENABLE_IBT);
BT_CR0_ENABLE_IBT,
bt_bmc->base + BT_CR0);
clr_b_busy(bt_bmc);
@ -508,6 +472,7 @@ static int bt_bmc_remove(struct platform_device *pdev)
static const struct of_device_id bt_bmc_match[] = {
{ .compatible = "aspeed,ast2400-ibt-bmc" },
{ .compatible = "aspeed,ast2500-ibt-bmc" },
{ .compatible = "aspeed,ast2600-ibt-bmc" },
{ },
};

View File

@ -247,11 +247,13 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) {
rv2 = -EMSGSIZE;
if (trunc)
if (trunc) {
rv2 = -EMSGSIZE;
msg->msg.data_len = rsp->msg.data_len;
else
} else {
rv = -EMSGSIZE;
goto recv_putback_on_err;
}
}
if (copy_to_user(rsp->msg.data,

View File

@ -0,0 +1,539 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver to talk to a remote management controller on IPMB.
*/
#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/ipmi_msgdefs.h>
#include <linux/ipmi_smi.h>
#define DEVICE_NAME "ipmi-ipmb"
static int bmcaddr = 0x20;
module_param(bmcaddr, int, 0644);
MODULE_PARM_DESC(bmcaddr, "Address to use for BMC.");
static unsigned int retry_time_ms = 250;
module_param(retry_time_ms, uint, 0644);
MODULE_PARM_DESC(max_retries, "Timeout time between retries, in milliseconds.");
static unsigned int max_retries = 1;
module_param(max_retries, uint, 0644);
MODULE_PARM_DESC(max_retries, "Max resends of a command before timing out.");
/* Add room for the two slave addresses, two checksums, and rqSeq. */
#define IPMB_MAX_MSG_LEN (IPMI_MAX_MSG_LENGTH + 5)
struct ipmi_ipmb_dev {
struct ipmi_smi *intf;
struct i2c_client *client;
struct ipmi_smi_handlers handlers;
bool ready;
u8 curr_seq;
u8 bmcaddr;
u32 retry_time_ms;
u32 max_retries;
struct ipmi_smi_msg *next_msg;
struct ipmi_smi_msg *working_msg;
/* Transmit thread. */
struct task_struct *thread;
struct semaphore wake_thread;
struct semaphore got_rsp;
spinlock_t lock;
bool stopping;
u8 xmitmsg[IPMB_MAX_MSG_LEN];
unsigned int xmitlen;
u8 rcvmsg[IPMB_MAX_MSG_LEN];
unsigned int rcvlen;
bool overrun;
};
static bool valid_ipmb(struct ipmi_ipmb_dev *iidev)
{
u8 *msg = iidev->rcvmsg;
u8 netfn;
if (iidev->overrun)
return false;
/* Minimum message size. */
if (iidev->rcvlen < 7)
return false;
/* Is it a response? */
netfn = msg[1] >> 2;
if (netfn & 1) {
/* Response messages have an added completion code. */
if (iidev->rcvlen < 8)
return false;
}
if (ipmb_checksum(msg, 3) != 0)
return false;
if (ipmb_checksum(msg + 3, iidev->rcvlen - 3) != 0)
return false;
return true;
}
static void ipmi_ipmb_check_msg_done(struct ipmi_ipmb_dev *iidev)
{
struct ipmi_smi_msg *imsg = NULL;
u8 *msg = iidev->rcvmsg;
bool is_cmd;
unsigned long flags;
if (iidev->rcvlen == 0)
return;
if (!valid_ipmb(iidev))
goto done;
is_cmd = ((msg[1] >> 2) & 1) == 0;
if (is_cmd) {
/* Ignore commands until we are up. */
if (!iidev->ready)
goto done;
/* It's a command, allocate a message for it. */
imsg = ipmi_alloc_smi_msg();
if (!imsg)
goto done;
imsg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT;
imsg->data_size = 0;
} else {
spin_lock_irqsave(&iidev->lock, flags);
if (iidev->working_msg) {
u8 seq = msg[4] >> 2;
bool xmit_rsp = (iidev->working_msg->data[0] >> 2) & 1;
/*
* Responses should carry the sequence we sent
* them with. If it's a transmitted response,
* ignore it. And if the message hasn't been
* transmitted, ignore it.
*/
if (!xmit_rsp && seq == iidev->curr_seq) {
iidev->curr_seq = (iidev->curr_seq + 1) & 0x3f;
imsg = iidev->working_msg;
iidev->working_msg = NULL;
}
}
spin_unlock_irqrestore(&iidev->lock, flags);
}
if (!imsg)
goto done;
if (imsg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
imsg->rsp[0] = msg[1]; /* NetFn/LUN */
/*
* Keep the source address, rqSeq. Drop the trailing
* checksum.
*/
memcpy(imsg->rsp + 1, msg + 3, iidev->rcvlen - 4);
imsg->rsp_size = iidev->rcvlen - 3;
} else {
imsg->rsp[0] = msg[1]; /* NetFn/LUN */
/*
* Skip the source address, rqSeq. Drop the trailing
* checksum.
*/
memcpy(imsg->rsp + 1, msg + 5, iidev->rcvlen - 6);
imsg->rsp_size = iidev->rcvlen - 5;
}
ipmi_smi_msg_received(iidev->intf, imsg);
if (!is_cmd)
up(&iidev->got_rsp);
done:
iidev->overrun = false;
iidev->rcvlen = 0;
}
/*
* The IPMB protocol only supports i2c writes so there is no need to
* support I2C_SLAVE_READ* events, except to know if the other end has
* issued a read without going to stop mode.
*/
static int ipmi_ipmb_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
switch (event) {
case I2C_SLAVE_WRITE_REQUESTED:
ipmi_ipmb_check_msg_done(iidev);
/*
* First byte is the slave address, to ease the checksum
* calculation.
*/
iidev->rcvmsg[0] = client->addr << 1;
iidev->rcvlen = 1;
break;
case I2C_SLAVE_WRITE_RECEIVED:
if (iidev->rcvlen >= sizeof(iidev->rcvmsg))
iidev->overrun = true;
else
iidev->rcvmsg[iidev->rcvlen++] = *val;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_STOP:
ipmi_ipmb_check_msg_done(iidev);
break;
case I2C_SLAVE_READ_PROCESSED:
break;
}
return 0;
}
static void ipmi_ipmb_send_response(struct ipmi_ipmb_dev *iidev,
struct ipmi_smi_msg *msg, u8 cc)
{
if ((msg->data[0] >> 2) & 1) {
/*
* It's a response being sent, we needto return a
* response response. Fake a send msg command
* response with channel 0. This will always be ipmb
* direct.
*/
msg->data[0] = (IPMI_NETFN_APP_REQUEST | 1) << 2;
msg->data[3] = IPMI_SEND_MSG_CMD;
msg->data[4] = cc;
msg->data_size = 5;
}
msg->rsp[0] = msg->data[0] | (1 << 2);
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
msg->rsp[1] = msg->data[1];
msg->rsp[2] = msg->data[2];
msg->rsp[3] = msg->data[3];
msg->rsp[4] = cc;
msg->rsp_size = 5;
} else {
msg->rsp[1] = msg->data[1];
msg->rsp[2] = cc;
msg->rsp_size = 3;
}
ipmi_smi_msg_received(iidev->intf, msg);
}
static void ipmi_ipmb_format_for_xmit(struct ipmi_ipmb_dev *iidev,
struct ipmi_smi_msg *msg)
{
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
iidev->xmitmsg[0] = msg->data[1];
iidev->xmitmsg[1] = msg->data[0];
memcpy(iidev->xmitmsg + 4, msg->data + 2, msg->data_size - 2);
iidev->xmitlen = msg->data_size + 2;
} else {
iidev->xmitmsg[0] = iidev->bmcaddr;
iidev->xmitmsg[1] = msg->data[0];
iidev->xmitmsg[4] = 0;
memcpy(iidev->xmitmsg + 5, msg->data + 1, msg->data_size - 1);
iidev->xmitlen = msg->data_size + 4;
}
iidev->xmitmsg[3] = iidev->client->addr << 1;
if (((msg->data[0] >> 2) & 1) == 0)
/* If it's a command, put in our own sequence number. */
iidev->xmitmsg[4] = ((iidev->xmitmsg[4] & 0x03) |
(iidev->curr_seq << 2));
/* Now add on the final checksums. */
iidev->xmitmsg[2] = ipmb_checksum(iidev->xmitmsg, 2);
iidev->xmitmsg[iidev->xmitlen] =
ipmb_checksum(iidev->xmitmsg + 3, iidev->xmitlen - 3);
iidev->xmitlen++;
}
static int ipmi_ipmb_thread(void *data)
{
struct ipmi_ipmb_dev *iidev = data;
while (!kthread_should_stop()) {
long ret;
struct i2c_msg i2c_msg;
struct ipmi_smi_msg *msg = NULL;
unsigned long flags;
unsigned int retries = 0;
/* Wait for a message to send */
ret = down_interruptible(&iidev->wake_thread);
if (iidev->stopping)
break;
if (ret)
continue;
spin_lock_irqsave(&iidev->lock, flags);
if (iidev->next_msg) {
msg = iidev->next_msg;
iidev->next_msg = NULL;
}
spin_unlock_irqrestore(&iidev->lock, flags);
if (!msg)
continue;
ipmi_ipmb_format_for_xmit(iidev, msg);
retry:
i2c_msg.len = iidev->xmitlen - 1;
if (i2c_msg.len > 32) {
ipmi_ipmb_send_response(iidev, msg,
IPMI_REQ_LEN_EXCEEDED_ERR);
continue;
}
i2c_msg.addr = iidev->xmitmsg[0] >> 1;
i2c_msg.flags = 0;
i2c_msg.buf = iidev->xmitmsg + 1;
/* Rely on i2c_transfer for a barrier. */
iidev->working_msg = msg;
ret = i2c_transfer(iidev->client->adapter, &i2c_msg, 1);
if ((msg->data[0] >> 2) & 1) {
/*
* It's a response, nothing will be returned
* by the other end.
*/
iidev->working_msg = NULL;
ipmi_ipmb_send_response(iidev, msg,
ret < 0 ? IPMI_BUS_ERR : 0);
continue;
}
if (ret < 0) {
iidev->working_msg = NULL;
ipmi_ipmb_send_response(iidev, msg, IPMI_BUS_ERR);
continue;
}
/* A command was sent, wait for its response. */
ret = down_timeout(&iidev->got_rsp,
msecs_to_jiffies(iidev->retry_time_ms));
/*
* Grab the message if we can. If the handler hasn't
* already handled it, the message will still be there.
*/
spin_lock_irqsave(&iidev->lock, flags);
msg = iidev->working_msg;
iidev->working_msg = NULL;
spin_unlock_irqrestore(&iidev->lock, flags);
if (!msg && ret) {
/*
* If working_msg is not set and we timed out,
* that means the message grabbed by
* check_msg_done before we could grab it
* here. Wait again for check_msg_done to up
* the semaphore.
*/
down(&iidev->got_rsp);
} else if (msg && ++retries <= iidev->max_retries) {
spin_lock_irqsave(&iidev->lock, flags);
iidev->working_msg = msg;
spin_unlock_irqrestore(&iidev->lock, flags);
goto retry;
}
if (msg)
ipmi_ipmb_send_response(iidev, msg, IPMI_TIMEOUT_ERR);
}
if (iidev->next_msg)
/* Return an unspecified error. */
ipmi_ipmb_send_response(iidev, iidev->next_msg, 0xff);
return 0;
}
static int ipmi_ipmb_start_processing(void *send_info,
struct ipmi_smi *new_intf)
{
struct ipmi_ipmb_dev *iidev = send_info;
iidev->intf = new_intf;
iidev->ready = true;
return 0;
}
static void ipmi_ipmb_stop_thread(struct ipmi_ipmb_dev *iidev)
{
if (iidev->thread) {
struct task_struct *t = iidev->thread;
iidev->thread = NULL;
iidev->stopping = true;
up(&iidev->wake_thread);
up(&iidev->got_rsp);
kthread_stop(t);
}
}
static void ipmi_ipmb_shutdown(void *send_info)
{
struct ipmi_ipmb_dev *iidev = send_info;
ipmi_ipmb_stop_thread(iidev);
}
static void ipmi_ipmb_sender(void *send_info,
struct ipmi_smi_msg *msg)
{
struct ipmi_ipmb_dev *iidev = send_info;
unsigned long flags;
spin_lock_irqsave(&iidev->lock, flags);
BUG_ON(iidev->next_msg);
iidev->next_msg = msg;
spin_unlock_irqrestore(&iidev->lock, flags);
up(&iidev->wake_thread);
}
static void ipmi_ipmb_request_events(void *send_info)
{
/* We don't fetch events here. */
}
static int ipmi_ipmb_remove(struct i2c_client *client)
{
struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
if (iidev->client) {
iidev->client = NULL;
i2c_slave_unregister(client);
}
ipmi_ipmb_stop_thread(iidev);
return 0;
}
static int ipmi_ipmb_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ipmi_ipmb_dev *iidev;
int rv;
iidev = devm_kzalloc(&client->dev, sizeof(*iidev), GFP_KERNEL);
if (!iidev)
return -ENOMEM;
if (of_property_read_u8(dev->of_node, "bmcaddr", &iidev->bmcaddr) != 0)
iidev->bmcaddr = bmcaddr;
if (iidev->bmcaddr == 0 || iidev->bmcaddr & 1) {
/* Can't have the write bit set. */
dev_notice(&client->dev,
"Invalid bmc address value %2.2x\n", iidev->bmcaddr);
return -EINVAL;
}
if (of_property_read_u32(dev->of_node, "retry-time",
&iidev->retry_time_ms) != 0)
iidev->retry_time_ms = retry_time_ms;
if (of_property_read_u32(dev->of_node, "max-retries",
&iidev->max_retries) != 0)
iidev->max_retries = max_retries;
i2c_set_clientdata(client, iidev);
client->flags |= I2C_CLIENT_SLAVE;
rv = i2c_slave_register(client, ipmi_ipmb_slave_cb);
if (rv)
return rv;
iidev->client = client;
iidev->handlers.flags = IPMI_SMI_CAN_HANDLE_IPMB_DIRECT;
iidev->handlers.start_processing = ipmi_ipmb_start_processing;
iidev->handlers.shutdown = ipmi_ipmb_shutdown;
iidev->handlers.sender = ipmi_ipmb_sender;
iidev->handlers.request_events = ipmi_ipmb_request_events;
spin_lock_init(&iidev->lock);
sema_init(&iidev->wake_thread, 0);
sema_init(&iidev->got_rsp, 0);
iidev->thread = kthread_run(ipmi_ipmb_thread, iidev,
"kipmb%4.4x", client->addr);
if (IS_ERR(iidev->thread)) {
rv = PTR_ERR(iidev->thread);
dev_notice(&client->dev,
"Could not start kernel thread: error %d\n", rv);
goto out_err;
}
rv = ipmi_register_smi(&iidev->handlers,
iidev,
&client->dev,
iidev->bmcaddr);
if (rv)
goto out_err;
return 0;
out_err:
ipmi_ipmb_remove(client);
return rv;
}
#ifdef CONFIG_OF
static const struct of_device_id of_ipmi_ipmb_match[] = {
{ .type = "ipmi", .compatible = DEVICE_NAME },
{},
};
MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match);
#else
#define of_ipmi_ipmb_match NULL
#endif
static const struct i2c_device_id ipmi_ipmb_id[] = {
{ DEVICE_NAME, 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id);
static struct i2c_driver ipmi_ipmb_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = DEVICE_NAME,
.of_match_table = of_ipmi_ipmb_match,
},
.probe = ipmi_ipmb_probe,
.remove = ipmi_ipmb_remove,
.id_table = ipmi_ipmb_id,
};
module_i2c_driver(ipmi_ipmb_driver);
MODULE_AUTHOR("Corey Minyard");
MODULE_DESCRIPTION("IPMI IPMB driver");
MODULE_LICENSE("GPL v2");

View File

@ -653,6 +653,11 @@ static int is_ipmb_bcast_addr(struct ipmi_addr *addr)
return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE;
}
static int is_ipmb_direct_addr(struct ipmi_addr *addr)
{
return addr->addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE;
}
static void free_recv_msg_list(struct list_head *q)
{
struct ipmi_recv_msg *msg, *msg2;
@ -805,6 +810,17 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
&& (ipmb_addr1->lun == ipmb_addr2->lun));
}
if (is_ipmb_direct_addr(addr1)) {
struct ipmi_ipmb_direct_addr *daddr1
= (struct ipmi_ipmb_direct_addr *) addr1;
struct ipmi_ipmb_direct_addr *daddr2
= (struct ipmi_ipmb_direct_addr *) addr2;
return daddr1->slave_addr == daddr2->slave_addr &&
daddr1->rq_lun == daddr2->rq_lun &&
daddr1->rs_lun == daddr2->rs_lun;
}
if (is_lan_addr(addr1)) {
struct ipmi_lan_addr *lan_addr1
= (struct ipmi_lan_addr *) addr1;
@ -843,6 +859,23 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len)
return 0;
}
if (is_ipmb_direct_addr(addr)) {
struct ipmi_ipmb_direct_addr *daddr = (void *) addr;
if (addr->channel != 0)
return -EINVAL;
if (len < sizeof(struct ipmi_ipmb_direct_addr))
return -EINVAL;
if (daddr->slave_addr & 0x01)
return -EINVAL;
if (daddr->rq_lun >= 4)
return -EINVAL;
if (daddr->rs_lun >= 4)
return -EINVAL;
return 0;
}
if (is_lan_addr(addr)) {
if (len < sizeof(struct ipmi_lan_addr))
return -EINVAL;
@ -862,6 +895,9 @@ unsigned int ipmi_addr_length(int addr_type)
|| (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
return sizeof(struct ipmi_ipmb_addr);
if (addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE)
return sizeof(struct ipmi_ipmb_direct_addr);
if (addr_type == IPMI_LAN_ADDR_TYPE)
return sizeof(struct ipmi_lan_addr);
@ -1710,7 +1746,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
}
EXPORT_SYMBOL(ipmi_unregister_for_cmd);
static unsigned char
unsigned char
ipmb_checksum(unsigned char *data, int size)
{
unsigned char csum = 0;
@ -1720,6 +1756,7 @@ ipmb_checksum(unsigned char *data, int size)
return -csum;
}
EXPORT_SYMBOL(ipmb_checksum);
static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg,
struct kernel_ipmi_msg *msg,
@ -2051,6 +2088,58 @@ out_err:
return rv;
}
static int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf,
struct ipmi_addr *addr,
long msgid,
struct kernel_ipmi_msg *msg,
struct ipmi_smi_msg *smi_msg,
struct ipmi_recv_msg *recv_msg,
unsigned char source_lun)
{
struct ipmi_ipmb_direct_addr *daddr;
bool is_cmd = !(recv_msg->msg.netfn & 0x1);
if (!(intf->handlers->flags & IPMI_SMI_CAN_HANDLE_IPMB_DIRECT))
return -EAFNOSUPPORT;
/* Responses must have a completion code. */
if (!is_cmd && msg->data_len < 1) {
ipmi_inc_stat(intf, sent_invalid_commands);
return -EINVAL;
}
if ((msg->data_len + 4) > IPMI_MAX_MSG_LENGTH) {
ipmi_inc_stat(intf, sent_invalid_commands);
return -EMSGSIZE;
}
daddr = (struct ipmi_ipmb_direct_addr *) addr;
if (daddr->rq_lun > 3 || daddr->rs_lun > 3) {
ipmi_inc_stat(intf, sent_invalid_commands);
return -EINVAL;
}
smi_msg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT;
smi_msg->msgid = msgid;
if (is_cmd) {
smi_msg->data[0] = msg->netfn << 2 | daddr->rs_lun;
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rq_lun;
} else {
smi_msg->data[0] = msg->netfn << 2 | daddr->rq_lun;
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rs_lun;
}
smi_msg->data[1] = daddr->slave_addr;
smi_msg->data[3] = msg->cmd;
memcpy(smi_msg->data + 4, msg->data, msg->data_len);
smi_msg->data_size = msg->data_len + 4;
smi_msg->user_data = recv_msg;
return 0;
}
static int i_ipmi_req_lan(struct ipmi_smi *intf,
struct ipmi_addr *addr,
long msgid,
@ -2240,6 +2329,9 @@ static int i_ipmi_request(struct ipmi_user *user,
rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
source_address, source_lun,
retries, retry_time_ms);
} else if (is_ipmb_direct_addr(addr)) {
rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg,
recv_msg, source_lun);
} else if (is_lan_addr(addr)) {
rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
source_lun, retries, retry_time_ms);
@ -2369,6 +2461,13 @@ static void bmc_device_id_handler(struct ipmi_smi *intf,
return;
}
if (msg->msg.data[0]) {
dev_warn(intf->si_dev, "device id fetch failed: 0x%2.2x\n",
msg->msg.data[0]);
intf->bmc->dyn_id_set = 0;
goto out;
}
rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id);
if (rv) {
@ -2384,7 +2483,7 @@ static void bmc_device_id_handler(struct ipmi_smi *intf,
smp_wmb();
intf->bmc->dyn_id_set = 1;
}
out:
wake_up(&intf->waitq);
}
@ -2617,7 +2716,7 @@ static ssize_t device_id_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 10, "%u\n", id.device_id);
return sysfs_emit(buf, "%u\n", id.device_id);
}
static DEVICE_ATTR_RO(device_id);
@ -2633,7 +2732,7 @@ static ssize_t provides_device_sdrs_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7);
return sysfs_emit(buf, "%u\n", (id.device_revision & 0x80) >> 7);
}
static DEVICE_ATTR_RO(provides_device_sdrs);
@ -2648,7 +2747,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
if (rv)
return rv;
return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F);
return sysfs_emit(buf, "%u\n", id.device_revision & 0x0F);
}
static DEVICE_ATTR_RO(revision);
@ -2664,7 +2763,7 @@ static ssize_t firmware_revision_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1,
return sysfs_emit(buf, "%u.%x\n", id.firmware_revision_1,
id.firmware_revision_2);
}
static DEVICE_ATTR_RO(firmware_revision);
@ -2681,7 +2780,7 @@ static ssize_t ipmi_version_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 20, "%u.%u\n",
return sysfs_emit(buf, "%u.%u\n",
ipmi_version_major(&id),
ipmi_version_minor(&id));
}
@ -2699,7 +2798,7 @@ static ssize_t add_dev_support_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 10, "0x%02x\n", id.additional_device_support);
return sysfs_emit(buf, "0x%02x\n", id.additional_device_support);
}
static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show,
NULL);
@ -2716,7 +2815,7 @@ static ssize_t manufacturer_id_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id);
return sysfs_emit(buf, "0x%6.6x\n", id.manufacturer_id);
}
static DEVICE_ATTR_RO(manufacturer_id);
@ -2732,7 +2831,7 @@ static ssize_t product_id_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 10, "0x%4.4x\n", id.product_id);
return sysfs_emit(buf, "0x%4.4x\n", id.product_id);
}
static DEVICE_ATTR_RO(product_id);
@ -2748,7 +2847,7 @@ static ssize_t aux_firmware_rev_show(struct device *dev,
if (rv)
return rv;
return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
return sysfs_emit(buf, "0x%02x 0x%02x 0x%02x 0x%02x\n",
id.aux_firmware_revision[3],
id.aux_firmware_revision[2],
id.aux_firmware_revision[1],
@ -2770,7 +2869,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
if (!guid_set)
return -ENOENT;
return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
return sysfs_emit(buf, "%pUl\n", &guid);
}
static DEVICE_ATTR_RO(guid);
@ -3794,6 +3893,123 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
return rv;
}
static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg)
{
struct cmd_rcvr *rcvr;
int rv = 0;
struct ipmi_user *user = NULL;
struct ipmi_ipmb_direct_addr *daddr;
struct ipmi_recv_msg *recv_msg;
unsigned char netfn = msg->rsp[0] >> 2;
unsigned char cmd = msg->rsp[3];
rcu_read_lock();
/* We always use channel 0 for direct messages. */
rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
if (rcvr) {
user = rcvr->user;
kref_get(&user->refcount);
} else
user = NULL;
rcu_read_unlock();
if (user == NULL) {
/* We didn't find a user, deliver an error response. */
ipmi_inc_stat(intf, unhandled_commands);
msg->data[0] = ((netfn + 1) << 2) | (msg->rsp[4] & 0x3);
msg->data[1] = msg->rsp[2];
msg->data[2] = msg->rsp[4] & ~0x3;
msg->data[3] = cmd;
msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
msg->data_size = 5;
rcu_read_lock();
if (!intf->in_shutdown) {
smi_send(intf, intf->handlers, msg, 0);
/*
* We used the message, so return the value
* that causes it to not be freed or
* queued.
*/
rv = -1;
}
rcu_read_unlock();
} else {
recv_msg = ipmi_alloc_recv_msg();
if (!recv_msg) {
/*
* We couldn't allocate memory for the
* message, so requeue it for handling
* later.
*/
rv = 1;
kref_put(&user->refcount, free_user);
} else {
/* Extract the source address from the data. */
daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
daddr->channel = 0;
daddr->slave_addr = msg->rsp[1];
daddr->rs_lun = msg->rsp[0] & 3;
daddr->rq_lun = msg->rsp[2] & 3;
/*
* Extract the rest of the message information
* from the IPMB header.
*/
recv_msg->user = user;
recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
recv_msg->msgid = (msg->rsp[2] >> 2);
recv_msg->msg.netfn = msg->rsp[0] >> 2;
recv_msg->msg.cmd = msg->rsp[3];
recv_msg->msg.data = recv_msg->msg_data;
recv_msg->msg.data_len = msg->rsp_size - 4;
memcpy(recv_msg->msg_data, msg->rsp + 4,
msg->rsp_size - 4);
if (deliver_response(intf, recv_msg))
ipmi_inc_stat(intf, unhandled_commands);
else
ipmi_inc_stat(intf, handled_commands);
}
}
return rv;
}
static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg)
{
struct ipmi_recv_msg *recv_msg;
struct ipmi_ipmb_direct_addr *daddr;
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
if (recv_msg == NULL) {
dev_warn(intf->si_dev,
"IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
return 0;
}
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
recv_msg->msgid = msg->msgid;
daddr = (struct ipmi_ipmb_direct_addr *) &recv_msg->addr;
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
daddr->channel = 0;
daddr->slave_addr = msg->rsp[1];
daddr->rq_lun = msg->rsp[0] & 3;
daddr->rs_lun = msg->rsp[2] & 3;
recv_msg->msg.netfn = msg->rsp[0] >> 2;
recv_msg->msg.cmd = msg->rsp[3];
memcpy(recv_msg->msg_data, &msg->rsp[4], msg->rsp_size - 4);
recv_msg->msg.data = recv_msg->msg_data;
recv_msg->msg.data_len = msg->rsp_size - 4;
deliver_local_response(intf, recv_msg);
return 0;
}
static int handle_lan_get_msg_rsp(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg)
{
@ -4219,18 +4435,40 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
static int handle_one_recv_msg(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg)
{
int requeue;
int requeue = 0;
int chan;
unsigned char cc;
bool is_cmd = !((msg->rsp[0] >> 2) & 1);
pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);
if ((msg->data_size >= 2)
if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
dev_warn(intf->si_dev,
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
return_unspecified:
/* Generate an error response for the message. */
msg->rsp[0] = msg->data[0] | (1 << 2);
msg->rsp[1] = msg->data[1];
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
msg->rsp_size = 3;
} else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
/* commands must have at least 3 bytes, responses 4. */
if (is_cmd && (msg->rsp_size < 3)) {
ipmi_inc_stat(intf, invalid_commands);
goto out;
}
if (!is_cmd && (msg->rsp_size < 4))
goto return_unspecified;
} else if ((msg->data_size >= 2)
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
&& (msg->user_data == NULL)) {
if (intf->in_shutdown)
goto free_msg;
goto out;
/*
* This is the local response to a command send, start
@ -4265,21 +4503,6 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
} else
/* The message was sent, start the timer. */
intf_start_seq_timer(intf, msg->msgid);
free_msg:
requeue = 0;
goto out;
} else if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
dev_warn(intf->si_dev,
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
/* Generate an error response for the message. */
msg->rsp[0] = msg->data[0] | (1 << 2);
msg->rsp[1] = msg->data[1];
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
msg->rsp_size = 3;
} else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))
|| (msg->rsp[1] != msg->data[1])) {
/*
@ -4291,39 +4514,46 @@ free_msg:
(msg->data[0] >> 2) | 1, msg->data[1],
msg->rsp[0] >> 2, msg->rsp[1]);
/* Generate an error response for the message. */
msg->rsp[0] = msg->data[0] | (1 << 2);
msg->rsp[1] = msg->data[1];
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
msg->rsp_size = 3;
goto return_unspecified;
}
if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
&& (msg->user_data != NULL)) {
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
if ((msg->data[0] >> 2) & 1) {
/* It's a response to a sent response. */
chan = 0;
cc = msg->rsp[4];
goto process_response_response;
}
if (is_cmd)
requeue = handle_ipmb_direct_rcv_cmd(intf, msg);
else
requeue = handle_ipmb_direct_rcv_rsp(intf, msg);
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
&& (msg->user_data != NULL)) {
/*
* It's a response to a response we sent. For this we
* deliver a send message response to the user.
*/
struct ipmi_recv_msg *recv_msg = msg->user_data;
requeue = 0;
if (msg->rsp_size < 2)
/* Message is too small to be correct. */
goto out;
struct ipmi_recv_msg *recv_msg;
chan = msg->data[2] & 0x0f;
if (chan >= IPMI_MAX_CHANNELS)
/* Invalid channel number */
goto out;
cc = msg->rsp[2];
process_response_response:
recv_msg = msg->user_data;
requeue = 0;
if (!recv_msg)
goto out;
recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
recv_msg->msg.data = recv_msg->msg_data;
recv_msg->msg_data[0] = cc;
recv_msg->msg.data_len = 1;
recv_msg->msg_data[0] = msg->rsp[2];
deliver_local_response(intf, recv_msg);
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
@ -4789,7 +5019,9 @@ static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);
static void free_smi_msg(struct ipmi_smi_msg *msg)
{
atomic_dec(&smi_msg_inuse_count);
kfree(msg);
/* Try to keep as much stuff out of the panic path as possible. */
if (!oops_in_progress)
kfree(msg);
}
struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
@ -4808,7 +5040,9 @@ EXPORT_SYMBOL(ipmi_alloc_smi_msg);
static void free_recv_msg(struct ipmi_recv_msg *msg)
{
atomic_dec(&recv_msg_inuse_count);
kfree(msg);
/* Try to keep as much stuff out of the panic path as possible. */
if (!oops_in_progress)
kfree(msg);
}
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
@ -4826,7 +5060,7 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
{
if (msg->user)
if (msg->user && !oops_in_progress)
kref_put(&msg->user->refcount, free_user);
msg->done(msg);
}

View File

@ -1603,7 +1603,7 @@ static ssize_t name##_show(struct device *dev, \
{ \
struct smi_info *smi_info = dev_get_drvdata(dev); \
\
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
return sysfs_emit(buf, "%u\n", smi_get_stat(smi_info, name)); \
} \
static DEVICE_ATTR_RO(name)
@ -1613,7 +1613,7 @@ static ssize_t type_show(struct device *dev,
{
struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
}
static DEVICE_ATTR_RO(type);
@ -1624,7 +1624,7 @@ static ssize_t interrupts_enabled_show(struct device *dev,
struct smi_info *smi_info = dev_get_drvdata(dev);
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
return snprintf(buf, 10, "%d\n", enabled);
return sysfs_emit(buf, "%d\n", enabled);
}
static DEVICE_ATTR_RO(interrupts_enabled);
@ -1646,7 +1646,7 @@ static ssize_t params_show(struct device *dev,
{
struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 200,
return sysfs_emit(buf,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type],
addr_space_to_str[smi_info->io.addr_space],

View File

@ -1190,7 +1190,7 @@ static ssize_t ipmi_##name##_show(struct device *dev, \
{ \
struct ssif_info *ssif_info = dev_get_drvdata(dev); \
\
return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\
return sysfs_emit(buf, "%u\n", ssif_get_stat(ssif_info, name));\
} \
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
@ -1198,7 +1198,7 @@ static ssize_t ipmi_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, 10, "ssif\n");
return sysfs_emit(buf, "ssif\n");
}
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);

View File

@ -342,13 +342,17 @@ static atomic_t msg_tofree = ATOMIC_INIT(0);
static DECLARE_COMPLETION(msg_wait);
static void msg_free_smi(struct ipmi_smi_msg *msg)
{
if (atomic_dec_and_test(&msg_tofree))
complete(&msg_wait);
if (atomic_dec_and_test(&msg_tofree)) {
if (!oops_in_progress)
complete(&msg_wait);
}
}
static void msg_free_recv(struct ipmi_recv_msg *msg)
{
if (atomic_dec_and_test(&msg_tofree))
complete(&msg_wait);
if (atomic_dec_and_test(&msg_tofree)) {
if (!oops_in_progress)
complete(&msg_wait);
}
}
static struct ipmi_smi_msg smi_msg = {
.done = msg_free_smi
@ -434,8 +438,10 @@ static int _ipmi_set_timeout(int do_heartbeat)
rv = __ipmi_set_timeout(&smi_msg,
&recv_msg,
&send_heartbeat_now);
if (rv)
if (rv) {
atomic_set(&msg_tofree, 0);
return rv;
}
wait_for_completion(&msg_wait);
@ -497,7 +503,7 @@ static void panic_halt_ipmi_heartbeat(void)
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
atomic_inc(&panic_done_count);
atomic_add(2, &panic_done_count);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
@ -507,7 +513,7 @@ static void panic_halt_ipmi_heartbeat(void)
&panic_halt_heartbeat_recv_msg,
1);
if (rv)
atomic_dec(&panic_done_count);
atomic_sub(2, &panic_done_count);
}
static struct ipmi_smi_msg panic_halt_smi_msg = {
@ -531,12 +537,12 @@ static void panic_halt_ipmi_set_timeout(void)
/* Wait for the messages to be free. */
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
atomic_inc(&panic_done_count);
atomic_add(2, &panic_done_count);
rv = __ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
if (rv) {
atomic_dec(&panic_done_count);
atomic_sub(2, &panic_done_count);
pr_warn("Unable to extend the watchdog timeout\n");
} else {
if (send_heartbeat_now)
@ -580,6 +586,7 @@ restart:
&recv_msg,
1);
if (rv) {
atomic_set(&msg_tofree, 0);
pr_warn("heartbeat send failure: %d\n", rv);
return rv;
}

View File

@ -73,10 +73,12 @@ static int kcs_bmc_serio_add_device(struct kcs_bmc_device *kcs_bmc)
struct serio *port;
priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Use kzalloc() as the allocation is cleaned up with kfree() via serio_unregister_port() */
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!(priv && port))
if (!port)
return -ENOMEM;
port->id.type = SERIO_8042;

View File

@ -335,4 +335,7 @@ extern int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data);
#define GET_DEVICE_ID_MAX_RETRY 5
/* Helper function for computing the IPMB checksum of some data. */
unsigned char ipmb_checksum(unsigned char *data, int size);
#endif /* __LINUX_IPMI_H */

View File

@ -38,6 +38,59 @@ struct ipmi_smi;
#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1)
#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2)
/*
* SMI messages
*
* When communicating with an SMI, messages come in two formats:
*
* * Normal (to a BMC over a BMC interface)
*
* * IPMB (over a IPMB to another MC)
*
* When normal, commands are sent using the format defined by a
* standard message over KCS (NetFn must be even):
*
* +-----------+-----+------+
* | NetFn/LUN | Cmd | Data |
* +-----------+-----+------+
*
* And responses, similarly, with an completion code added (NetFn must
* be odd):
*
* +-----------+-----+------+------+
* | NetFn/LUN | Cmd | CC | Data |
* +-----------+-----+------+------+
*
* With normal messages, only commands are sent and only responses are
* received.
*
* In IPMB mode, we are acting as an IPMB device. Commands will be in
* the following format (NetFn must be even):
*
* +-------------+------+-------------+-----+------+
* | NetFn/rsLUN | Addr | rqSeq/rqLUN | Cmd | Data |
* +-------------+------+-------------+-----+------+
*
* Responses will using the following format:
*
* +-------------+------+-------------+-----+------+------+
* | NetFn/rqLUN | Addr | rqSeq/rsLUN | Cmd | CC | Data |
* +-------------+------+-------------+-----+------+------+
*
* This is similar to the format defined in the IPMB manual section
* 2.11.1 with the checksums and the first address removed. Also, the
* address is always the remote address.
*
* IPMB messages can be commands and responses in both directions.
* Received commands are handled as received commands from the message
* queue.
*/
enum ipmi_smi_msg_type {
IPMI_SMI_MSG_TYPE_NORMAL = 0,
IPMI_SMI_MSG_TYPE_IPMB_DIRECT
};
/*
* Messages to/from the lower layer. The smi interface will take one
* of these to send. After the send has occurred and a response has
@ -54,6 +107,8 @@ struct ipmi_smi;
struct ipmi_smi_msg {
struct list_head link;
enum ipmi_smi_msg_type type;
long msgid;
void *user_data;
@ -73,6 +128,10 @@ struct ipmi_smi_msg {
struct ipmi_smi_handlers {
struct module *owner;
/* Capabilities of the SMI. */
#define IPMI_SMI_CAN_HANDLE_IPMB_DIRECT (1 << 0)
unsigned int flags;
/*
* The low-level interface cannot start sending messages to
* the upper layer until this function is called. This may

View File

@ -80,6 +80,20 @@ struct ipmi_ipmb_addr {
unsigned char lun;
};
/*
* Used for messages received directly from an IPMB that have not gone
* through a MC. This is for systems that sit right on an IPMB so
* they can receive commands and respond to them.
*/
#define IPMI_IPMB_DIRECT_ADDR_TYPE 0x81
struct ipmi_ipmb_direct_addr {
int addr_type;
short channel;
unsigned char slave_addr;
unsigned char rs_lun;
unsigned char rq_lun;
};
/*
* A LAN Address. This is an address to/from a LAN interface bridged
* by the BMC, not an address actually out on the LAN.
@ -158,7 +172,7 @@ struct kernel_ipmi_msg {
* is used for the receive in-kernel interface and in the receive
* IOCTL.
*
* The "IPMI_RESPONSE_RESPNOSE_TYPE" is a little strange sounding, but
* The "IPMI_RESPONSE_RESPONSE_TYPE" is a little strange sounding, but
* it allows you to get the message results when you send a response
* message.
*/