diff options
Diffstat (limited to 'drivers/char')
29 files changed, 1099 insertions, 430 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 239eca4d6805..814b3d0ca7b7 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -63,7 +63,7 @@ config HW_RANDOM_AMD config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" - depends on ARCH_AT91 && HAVE_CLK && OF + depends on (ARCH_AT91 || COMPILE_TEST) && HAVE_CLK && OF default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -87,7 +87,7 @@ config HW_RANDOM_BA431 config HW_RANDOM_BCM2835 tristate "Broadcom BCM2835/BCM63xx Random Number Generator support" depends on ARCH_BCM2835 || ARCH_BCM_NSP || ARCH_BCM_5301X || \ - ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC + ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -100,7 +100,7 @@ config HW_RANDOM_BCM2835 config HW_RANDOM_IPROC_RNG200 tristate "Broadcom iProc/STB RNG200 support" - depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB + depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the RNG200 @@ -165,7 +165,7 @@ config HW_RANDOM_IXP4XX config HW_RANDOM_OMAP tristate "OMAP Random Number Generator support" - depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || ARCH_MVEBU || ARCH_K3 + depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || ARCH_MVEBU || ARCH_K3 || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -179,7 +179,7 @@ config HW_RANDOM_OMAP config HW_RANDOM_OMAP3_ROM tristate "OMAP3 ROM Random Number Generator support" - depends on ARCH_OMAP3 + depends on ARCH_OMAP3 || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the Random Number @@ -298,7 +298,7 @@ config HW_RANDOM_INGENIC_TRNG config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" - depends on ARCH_NOMADIK + depends on ARCH_NOMADIK || COMPILE_TEST default HW_RANDOM help This driver provides kernel-side support for the Random Number diff --git a/drivers/char/hw_random/ixp4xx-rng.c b/drivers/char/hw_random/ixp4xx-rng.c index 188854dd16a9..7df5e9f7519d 100644 --- a/drivers/char/hw_random/ixp4xx-rng.c +++ b/drivers/char/hw_random/ixp4xx-rng.c @@ -42,13 +42,11 @@ static int ixp4xx_rng_probe(struct platform_device *pdev) { void __iomem * rng_base; struct device *dev = &pdev->dev; - struct resource *res; if (!cpu_is_ixp46x()) /* includes IXP455 */ return -ENOSYS; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rng_base = devm_ioremap_resource(dev, res); + rng_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rng_base)) return PTR_ERR(rng_base); diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c index e446236e81f2..8bb30282ca46 100644 --- a/drivers/char/hw_random/meson-rng.c +++ b/drivers/char/hw_random/meson-rng.c @@ -54,9 +54,10 @@ static int meson_rng_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->core_clk = devm_clk_get(dev, "core"); + data->core_clk = devm_clk_get_optional(dev, "core"); if (IS_ERR(data->core_clk)) - data->core_clk = NULL; + return dev_err_probe(dev, PTR_ERR(data->core_clk), + "Failed to get core clock\n"); if (data->core_clk) { ret = clk_prepare_enable(data->core_clk); diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index 8ad7b515a51b..6c00ea008555 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -166,8 +166,13 @@ static int mtk_rng_runtime_resume(struct device *dev) return mtk_rng_init(&priv->rng); } -static UNIVERSAL_DEV_PM_OPS(mtk_rng_pm_ops, mtk_rng_runtime_suspend, - mtk_rng_runtime_resume, NULL); +static const struct dev_pm_ops mtk_rng_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_rng_runtime_suspend, + mtk_rng_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + #define MTK_RNG_PM_OPS (&mtk_rng_pm_ops) #else /* CONFIG_PM */ #define MTK_RNG_PM_OPS NULL diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c index 7c673afd7241..2beaa35c0d74 100644 --- a/drivers/char/hw_random/s390-trng.c +++ b/drivers/char/hw_random/s390-trng.c @@ -111,7 +111,7 @@ static ssize_t trng_counter_show(struct device *dev, #if IS_ENABLED(CONFIG_ARCH_RANDOM) u64 arch_counter = atomic64_read(&s390_arch_random_counter); - return snprintf(buf, PAGE_SIZE, + return sysfs_emit(buf, "trng: %llu\n" "hwrng: %llu\n" "arch: %llu\n" @@ -119,7 +119,7 @@ static ssize_t trng_counter_show(struct device *dev, dev_counter, hwrng_counter, arch_counter, dev_counter + hwrng_counter + arch_counter); #else - return snprintf(buf, PAGE_SIZE, + return sysfs_emit(buf, "trng: %llu\n" "hwrng: %llu\n" "total: %llu\n", diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index a90001e02bf7..0a7dde135db1 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -18,13 +18,20 @@ static DEFINE_IDA(rng_index_ida); struct virtrng_info { struct hwrng hwrng; struct virtqueue *vq; - struct completion have_data; char name[25]; - unsigned int data_avail; int index; - bool busy; bool hwrng_register_done; bool hwrng_removed; + /* data transfer */ + struct completion have_data; + unsigned int data_avail; + unsigned int data_idx; + /* minimal size returned by rng_buffer_size() */ +#if SMP_CACHE_BYTES < 32 + u8 data[32]; +#else + u8 data[SMP_CACHE_BYTES]; +#endif }; static void random_recv_done(struct virtqueue *vq) @@ -35,54 +42,88 @@ static void random_recv_done(struct virtqueue *vq) if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) return; + vi->data_idx = 0; + complete(&vi->have_data); } -/* The host will fill any buffer we give it with sweet, sweet randomness. */ -static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size) +static void request_entropy(struct virtrng_info *vi) { struct scatterlist sg; - sg_init_one(&sg, buf, size); + reinit_completion(&vi->have_data); + vi->data_avail = 0; + vi->data_idx = 0; + + sg_init_one(&sg, vi->data, sizeof(vi->data)); /* There should always be room for one buffer. */ - virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); + virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL); virtqueue_kick(vi->vq); } +static unsigned int copy_data(struct virtrng_info *vi, void *buf, + unsigned int size) +{ + size = min_t(unsigned int, size, vi->data_avail); + memcpy(buf, vi->data + vi->data_idx, size); + vi->data_idx += size; + vi->data_avail -= size; + if (vi->data_avail == 0) + request_entropy(vi); + return size; +} + static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { int ret; struct virtrng_info *vi = (struct virtrng_info *)rng->priv; + unsigned int chunk; + size_t read; if (vi->hwrng_removed) return -ENODEV; - if (!vi->busy) { - vi->busy = true; - reinit_completion(&vi->have_data); - register_buffer(vi, buf, size); + read = 0; + + /* copy available data */ + if (vi->data_avail) { + chunk = copy_data(vi, buf, size); + size -= chunk; + read += chunk; } if (!wait) - return 0; - - ret = wait_for_completion_killable(&vi->have_data); - if (ret < 0) - return ret; + return read; + + /* We have already copied available entropy, + * so either size is 0 or data_avail is 0 + */ + while (size != 0) { + /* data_avail is 0 but a request is pending */ + ret = wait_for_completion_killable(&vi->have_data); + if (ret < 0) + return ret; + /* if vi->data_avail is 0, we have been interrupted + * by a cleanup, but buffer stays in the queue + */ + if (vi->data_avail == 0) + return read; - vi->busy = false; + chunk = copy_data(vi, buf + read, size); + size -= chunk; + read += chunk; + } - return vi->data_avail; + return read; } static void virtio_cleanup(struct hwrng *rng) { struct virtrng_info *vi = (struct virtrng_info *)rng->priv; - if (vi->busy) - wait_for_completion(&vi->have_data); + complete(&vi->have_data); } static int probe_common(struct virtio_device *vdev) @@ -118,6 +159,9 @@ static int probe_common(struct virtio_device *vdev) goto err_find; } + /* we always have a pending entropy request */ + request_entropy(vi); + return 0; err_find: @@ -133,9 +177,9 @@ static void remove_common(struct virtio_device *vdev) vi->hwrng_removed = true; vi->data_avail = 0; + vi->data_idx = 0; complete(&vi->have_data); vdev->config->reset(vdev); - vi->busy = false; if (vi->hwrng_register_done) hwrng_unregister(&vi->hwrng); vdev->config->del_vqs(vdev); diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 249b31197eea..b061e6b513ed 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -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' diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 84f47d18007f..7ce790efad92 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -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 diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index 6e3d247b55d1..7450904e330a 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -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, ®); - 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" }, { }, }; diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 3dd1d5abb298..d160fa4c73fe 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -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, diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c new file mode 100644 index 000000000000..ba0c2d2c6bbe --- /dev/null +++ b/drivers/char/ipmi/ipmi_ipmb.c @@ -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"); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index e96cb5c4f97a..deed355422f4 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -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); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6f3272b58ced..64dedb3ef8ec 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -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], diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 20d5af92966d..0c62e578749e 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -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); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index e4ff3b50de7f..883b4a341012 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -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; } diff --git a/drivers/char/ipmi/kcs_bmc_serio.c b/drivers/char/ipmi/kcs_bmc_serio.c index 7948cabde50b..7e2067628a6c 100644 --- a/drivers/char/ipmi/kcs_bmc_serio.c +++ b/drivers/char/ipmi/kcs_bmc_serio.c @@ -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; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1c596b5cdb27..cc296f0823bd 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -495,6 +495,10 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter) written += n; if (signal_pending(current)) return written ? written : -ERESTARTSYS; + if (!need_resched()) + continue; + if (iocb->ki_flags & IOCB_NOWAIT) + return written ? written : -EAGAIN; cond_resched(); } return written; @@ -696,11 +700,11 @@ static const struct memdev { #ifdef CONFIG_DEVMEM [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, #endif - [3] = { "null", 0666, &null_fops, 0 }, + [3] = { "null", 0666, &null_fops, FMODE_NOWAIT }, #ifdef CONFIG_DEVPORT [4] = { "port", 0, &port_fops, 0 }, #endif - [5] = { "zero", 0666, &zero_fops, 0 }, + [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT }, [7] = { "full", 0666, &full_fops, 0 }, [8] = { "random", 0666, &random_fops, 0 }, [9] = { "urandom", 0666, &urandom_fops, 0 }, diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 8f1bce0b4fe5..adaec8fd4b16 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -116,8 +116,9 @@ struct cm4000_dev { wait_queue_head_t atrq; /* wait for ATR valid */ wait_queue_head_t readq; /* used by write to wake blk.read */ - /* warning: do not move this fields. + /* warning: do not move this struct group. * initialising to zero depends on it - see ZERO_DEV below. */ + struct_group(init, unsigned char atr_csum; unsigned char atr_len_retry; unsigned short atr_len; @@ -140,12 +141,10 @@ struct cm4000_dev { struct timer_list timer; /* used to keep monitor running */ int monitor_running; + ); }; -#define ZERO_DEV(dev) \ - memset(&dev->atr_csum,0, \ - sizeof(struct cm4000_dev) - \ - offsetof(struct cm4000_dev, atr_csum)) +#define ZERO_DEV(dev) memset(&((dev)->init), 0, sizeof((dev)->init)) static struct pcmcia_device *dev_table[CM4000_MAX_DEV]; static struct class *cmm_class; diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index d6ba644f6b00..4a5516406c22 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -76,7 +76,7 @@ config TCG_TIS_SPI_CR50 config TCG_TIS_SYNQUACER tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface (MMIO - SynQuacer)" - depends on ARCH_SYNQUACER + depends on ARCH_SYNQUACER || COMPILE_TEST select TCG_TIS_CORE help If you have a TPM security chip that is compliant with the diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 784b8b3cb903..97e916856cf3 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -455,6 +455,9 @@ static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp, if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES) return 0; + if (be32_to_cpu(data->count) > (UINT_MAX - TPM_HEADER_SIZE - 9) / 4) + return -EFAULT; + if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count)) return -EFAULT; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 69579efb247b..b2659a4c4016 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -48,6 +48,7 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, wait_queue_head_t *queue, bool check_cancel) { + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop; long rc; u8 status; @@ -80,8 +81,8 @@ again: } } else { do { - usleep_range(TPM_TIMEOUT_USECS_MIN, - TPM_TIMEOUT_USECS_MAX); + usleep_range(priv->timeout_min, + priv->timeout_max); status = chip->ops->status(chip); if ((status & mask) == mask) return 0; @@ -945,7 +946,22 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX); chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX); chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX); + priv->timeout_min = TPM_TIMEOUT_USECS_MIN; + priv->timeout_max = TPM_TIMEOUT_USECS_MAX; priv->phy_ops = phy_ops; + + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); + if (rc < 0) + goto out_err; + + priv->manufacturer_id = vendor; + + if (priv->manufacturer_id == TPM_VID_ATML && + !(chip->flags & TPM_CHIP_FLAG_TPM2)) { + priv->timeout_min = TIS_TIMEOUT_MIN_ATML; + priv->timeout_max = TIS_TIMEOUT_MAX_ATML; + } + dev_set_drvdata(&chip->dev, priv); if (is_bsw()) { @@ -988,12 +1004,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (rc) goto out_err; - rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); - if (rc < 0) - goto out_err; - - priv->manufacturer_id = vendor; - rc = tpm_tis_read8(priv, TPM_RID(0), &rid); if (rc < 0) goto out_err; diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index b2a3c6c72882..3be24f221e32 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -54,6 +54,8 @@ enum tis_defaults { TIS_MEM_LEN = 0x5000, TIS_SHORT_TIMEOUT = 750, /* ms */ TIS_LONG_TIMEOUT = 2000, /* 2 sec */ + TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */ + TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */ }; /* Some timeout values are needed before it is known whether the chip is @@ -98,6 +100,8 @@ struct tpm_tis_data { wait_queue_head_t read_queue; const struct tpm_tis_phy_ops *phy_ops; unsigned short rng_quality; + unsigned int timeout_min; /* usecs */ + unsigned int timeout_max; /* usecs */ }; struct tpm_tis_phy_ops { diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c index 54584b4b00d1..aaa59a00eeae 100644 --- a/drivers/char/tpm/tpm_tis_spi_main.c +++ b/drivers/char/tpm/tpm_tis_spi_main.c @@ -267,6 +267,7 @@ static const struct spi_device_id tpm_tis_spi_id[] = { { "st33htpm-spi", (unsigned long)tpm_tis_spi_probe }, { "slb9670", (unsigned long)tpm_tis_spi_probe }, { "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe }, + { "tpm_tis-spi", (unsigned long)tpm_tis_spi_probe }, { "cr50", (unsigned long)cr50_spi_probe }, {} }; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 7eaf303a7a86..660c5c388c29 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -28,6 +28,7 @@ #include "../tty/hvc/hvc_console.h" #define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC) +#define VIRTCONS_MAX_PORTS 0x8000 /* * This is a global struct for storing common data for all the devices @@ -2036,6 +2037,14 @@ static int virtcons_probe(struct virtio_device *vdev) virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT, struct virtio_console_config, max_nr_ports, &portdev->max_nr_ports) == 0) { + if (portdev->max_nr_ports == 0 || + portdev->max_nr_ports > VIRTCONS_MAX_PORTS) { + dev_err(&vdev->dev, + "Invalidate max_nr_ports %d", + portdev->max_nr_ports); + err = -EINVAL; + goto free; + } multiport = true; } diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index c63ffc56637c..51de7cbc579e 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -87,13 +87,8 @@ struct xilly_channel { }; struct xilly_endpoint { - /* - * One of pdev and dev is always NULL, and the other is a valid - * pointer, depending on the type of device - */ - struct pci_dev *pdev; struct device *dev; - struct xilly_endpoint_hardware *ephw; + struct module *owner; int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */ __iomem void *registers; @@ -113,25 +108,8 @@ struct xilly_endpoint { unsigned int msg_buf_size; }; -struct xilly_endpoint_hardware { - struct module *owner; - void (*hw_sync_sgl_for_cpu)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - void (*hw_sync_sgl_for_device)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - int (*map_single)(struct xilly_endpoint *, - void *, - size_t, - int, - dma_addr_t *); -}; - struct xilly_mapping { - void *device; + struct device *device; dma_addr_t dma_addr; size_t size; int direction; @@ -139,10 +117,7 @@ struct xilly_mapping { irqreturn_t xillybus_isr(int irq, void *data); -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, - struct xilly_endpoint_hardware - *ephw); +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev); int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint); diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 931d0bf4cec6..11b7c4749274 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -122,10 +122,8 @@ irqreturn_t xillybus_isr(int irq, void *data) buf = ep->msgbuf_addr; buf_size = ep->msg_buf_size/sizeof(u32); - ep->ephw->hw_sync_sgl_for_cpu(ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr, + ep->msg_buf_size, DMA_FROM_DEVICE); for (i = 0; i < buf_size; i += 2) { if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) { @@ -140,11 +138,10 @@ irqreturn_t xillybus_isr(int irq, void *data) dev_err(ep->dev, "Lost sync with interrupt messages. Stopping.\n"); } else { - ep->ephw->hw_sync_sgl_for_device( - ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(ep->dev, + ep->msgbuf_dma_addr, + ep->msg_buf_size, + DMA_FROM_DEVICE); iowrite32(0x01, /* Message NACK */ ep->registers + fpga_msg_ctrl_reg); @@ -275,10 +272,8 @@ irqreturn_t xillybus_isr(int irq, void *data) } } - ep->ephw->hw_sync_sgl_for_device(ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr, + ep->msg_buf_size, DMA_FROM_DEVICE); ep->msg_counter = (ep->msg_counter + 1) & 0xf; ep->failed_messages = 0; @@ -304,6 +299,47 @@ struct xilly_alloc_state { u32 regdirection; }; +static void xilly_unmap(void *ptr) +{ + struct xilly_mapping *data = ptr; + + dma_unmap_single(data->device, data->dma_addr, + data->size, data->direction); + + kfree(ptr); +} + +static int xilly_map_single(struct xilly_endpoint *ep, + void *ptr, + size_t size, + int direction, + dma_addr_t *ret_dma_handle + ) +{ + dma_addr_t addr; + struct xilly_mapping *this; + + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (!this) + return -ENOMEM; + + addr = dma_map_single(ep->dev, ptr, size, direction); + + if (dma_mapping_error(ep->dev, addr)) { + kfree(this); + return -ENODEV; + } + + this->device = ep->dev; + this->dma_addr = addr; + this->size = size; + this->direction = direction; + + *ret_dma_handle = addr; + + return devm_add_action_or_reset(ep->dev, xilly_unmap, this); +} + static int xilly_get_dma_buffers(struct xilly_endpoint *ep, struct xilly_alloc_state *s, struct xilly_buffer **buffers, @@ -355,9 +391,9 @@ static int xilly_get_dma_buffers(struct xilly_endpoint *ep, s->left_of_salami = allocsize; } - rc = ep->ephw->map_single(ep, s->salami, - bytebufsize, s->direction, - &dma_addr); + rc = xilly_map_single(ep, s->salami, + bytebufsize, s->direction, + &dma_addr); if (rc) return rc; @@ -620,11 +656,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint) return -ENODEV; } - endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->wr_buffers[0]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->wr_buffers[0]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) { dev_err(endpoint->dev, @@ -735,11 +770,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf, if (!empty) { /* Go on, now without the spinlock */ if (bufpos == 0) /* Position zero means it's virgin */ - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->wr_buffers[bufidx]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->wr_buffers[bufidx]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); if (copy_to_user( userbuf, @@ -751,11 +785,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf, bytes_done += howmany; if (bufferdone) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->wr_buffers[bufidx]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->wr_buffers[bufidx]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); /* * Tell FPGA the buffer is done with. It's an @@ -1055,11 +1088,10 @@ static int xillybus_myflush(struct xilly_channel *channel, long timeout) else channel->rd_host_buf_idx++; - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); mutex_lock(&channel->endpoint->register_mutex); @@ -1275,11 +1307,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, if ((bufpos == 0) || /* Zero means it's virgin */ (channel->rd_leftovers[3] != 0)) { - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); /* Virgin, but leftovers are due */ for (i = 0; i < bufpos; i++) @@ -1297,11 +1328,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, bytes_done += howmany; if (bufferdone) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); mutex_lock(&channel->endpoint->register_mutex); @@ -1772,10 +1802,7 @@ static const struct file_operations xillybus_fops = { .poll = xillybus_poll, }; -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, - struct xilly_endpoint_hardware - *ephw) +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev) { struct xilly_endpoint *endpoint; @@ -1783,9 +1810,7 @@ struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, if (!endpoint) return NULL; - endpoint->pdev = pdev; endpoint->dev = dev; - endpoint->ephw = ephw; endpoint->msg_counter = 0x0b; endpoint->failed_messages = 0; endpoint->fatal_error = 0; @@ -1912,7 +1937,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) goto failed_idt; rc = xillybus_init_chrdev(dev, &xillybus_fops, - endpoint->ephw->owner, endpoint, + endpoint->owner, endpoint, idt_handle.names, idt_handle.names_len, endpoint->num_channels, diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 1a20b286fd1d..e5372e45d211 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -31,102 +31,22 @@ static const struct of_device_id xillybus_of_match[] = { MODULE_DEVICE_TABLE(of, xillybus_of_match); -static void xilly_dma_sync_single_for_cpu_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_cpu(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_for_device_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_device(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_nop(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ -} - -static void xilly_of_unmap(void *ptr) -{ - struct xilly_mapping *data = ptr; - - dma_unmap_single(data->device, data->dma_addr, - data->size, data->direction); - - kfree(ptr); -} - -static int xilly_map_single_of(struct xilly_endpoint *ep, - void *ptr, - size_t size, - int direction, - dma_addr_t *ret_dma_handle - ) -{ - dma_addr_t addr; - struct xilly_mapping *this; - - this = kzalloc(sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - addr = dma_map_single(ep->dev, ptr, size, direction); - - if (dma_mapping_error(ep->dev, addr)) { - kfree(this); - return -ENODEV; - } - - this->device = ep->dev; - this->dma_addr = addr; - this->size = size; - this->direction = direction; - - *ret_dma_handle = addr; - - return devm_add_action_or_reset(ep->dev, xilly_of_unmap, this); -} - -static struct xilly_endpoint_hardware of_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of, - .map_single = xilly_map_single_of, -}; - -static struct xilly_endpoint_hardware of_hw_coherent = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop, - .hw_sync_sgl_for_device = xilly_dma_sync_single_nop, - .map_single = xilly_map_single_of, -}; - static int xilly_drv_probe(struct platform_device *op) { struct device *dev = &op->dev; struct xilly_endpoint *endpoint; int rc; int irq; - struct xilly_endpoint_hardware *ephw = &of_hw; - if (of_property_read_bool(dev->of_node, "dma-coherent")) - ephw = &of_hw_coherent; - - endpoint = xillybus_init_endpoint(NULL, dev, ephw); + endpoint = xillybus_init_endpoint(dev); if (!endpoint) return -ENOMEM; dev_set_drvdata(dev, endpoint); + endpoint->owner = THIS_MODULE; + endpoint->registers = devm_platform_ioremap_resource(op, 0); if (IS_ERR(endpoint->registers)) return PTR_ERR(endpoint->registers); diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index bdf1c366b4fc..9858711e3e79 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -32,110 +32,21 @@ static const struct pci_device_id xillyids[] = { { /* End: all zeroes */ } }; -static int xilly_pci_direction(int direction) -{ - switch (direction) { - case DMA_TO_DEVICE: - return PCI_DMA_TODEVICE; - case DMA_FROM_DEVICE: - return PCI_DMA_FROMDEVICE; - default: - return PCI_DMA_BIDIRECTIONAL; - } -} - -static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - pci_dma_sync_single_for_cpu(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); -} - -static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - pci_dma_sync_single_for_device(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); -} - -static void xilly_pci_unmap(void *ptr) -{ - struct xilly_mapping *data = ptr; - - pci_unmap_single(data->device, data->dma_addr, - data->size, data->direction); - - kfree(ptr); -} - -/* - * Map either through the PCI DMA mapper or the non_PCI one. Behind the - * scenes exactly the same functions are called with the same parameters, - * but that can change. - */ - -static int xilly_map_single_pci(struct xilly_endpoint *ep, - void *ptr, - size_t size, - int direction, - dma_addr_t *ret_dma_handle - ) -{ - int pci_direction; - dma_addr_t addr; - struct xilly_mapping *this; - - this = kzalloc(sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - pci_direction = xilly_pci_direction(direction); - - addr = pci_map_single(ep->pdev, ptr, size, pci_direction); - - if (pci_dma_mapping_error(ep->pdev, addr)) { - kfree(this); - return -ENODEV; - } - - this->device = ep->pdev; - this->dma_addr = addr; - this->size = size; - this->direction = pci_direction; - - *ret_dma_handle = addr; - - return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this); -} - -static struct xilly_endpoint_hardware pci_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, - .map_single = xilly_map_single_pci, -}; - static int xilly_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct xilly_endpoint *endpoint; int rc; - endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); + endpoint = xillybus_init_endpoint(&pdev->dev); if (!endpoint) return -ENOMEM; pci_set_drvdata(pdev, endpoint); + endpoint->owner = THIS_MODULE; + rc = pcim_enable_device(pdev); if (rc) { dev_err(endpoint->dev, @@ -185,9 +96,9 @@ static int xilly_probe(struct pci_dev *pdev, * So go for the 64-bit mask only when failing is the other option. */ - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { endpoint->dma_using_dac = 0; - } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + } else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) { endpoint->dma_using_dac = 1; } else { dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c index e7f88f35c702..dc3551796e5e 100644 --- a/drivers/char/xillybus/xillyusb.c +++ b/drivers/char/xillybus/xillyusb.c @@ -1912,6 +1912,7 @@ static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev) dealloc: endpoint_dealloc(xdev->msg_ep); /* Also frees FIFO mem if allocated */ + xdev->msg_ep = NULL; return -ENOMEM; } |