diff options
Diffstat (limited to 'drivers/char')
27 files changed, 476 insertions, 161 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 466ebd84ad17..3e866885a405 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -291,7 +291,7 @@ config RTC and set the RTC in an SMP compatible fashion. If you think you have a use for such a device (such as periodic data - sampling), then say Y here, and read <file:Documentation/rtc.txt> + sampling), then say Y here, and read <file:Documentation/admin-guide/rtc.rst> for details. To compile this driver as a module, choose M here: the @@ -313,7 +313,7 @@ config JS_RTC /dev/rtc. If you think you have a use for such a device (such as periodic data - sampling), then say Y here, and read <file:Documentation/rtc.txt> + sampling), then say Y here, and read <file:Documentation/admin-guide/rtc.rst> for details. To compile this driver as a module, choose M here: the @@ -382,7 +382,7 @@ config SONYPI Device which can be found in many (all ?) Sony Vaio laptops. If you have one of those laptops, read - <file:Documentation/laptops/sonypi.txt>, and say Y or M here. + <file:Documentation/admin-guide/laptops/sonypi.rst>, and say Y or M here. To compile this driver as a module, choose M here: the module will be called sonypi. diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 658664a5a5aa..df1edb5ec0ad 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1311,8 +1311,7 @@ static void ipi_handler(void *null) void global_cache_flush(void) { - if (on_each_cpu(ipi_handler, NULL, 1) != 0) - panic(PFX "timed out waiting for the other CPUs!\n"); + on_each_cpu(ipi_handler, NULL, 1); } EXPORT_SYMBOL(global_cache_flush); diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 3695773ce7c3..84d9adbb62f6 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HP zx1 AGPGART routines. * * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. * Bjorn Helgaas <bjorn.helgaas@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/acpi.h> diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 15f2e7025b78..ed3c4c42fc23 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HP Quicksilver AGP GART routines * @@ -6,11 +7,6 @@ * Based on drivers/char/agpgart/hp-agp.c which is * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. * Bjorn Helgaas <bjorn.helgaas@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/module.h> diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 167e7370d43a..e5e5333f302d 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -134,7 +134,7 @@ static int bsr_mmap(struct file *filp, struct vm_area_struct *vma) return 0; } -static int bsr_open(struct inode * inode, struct file * filp) +static int bsr_open(struct inode *inode, struct file *filp) { struct cdev *cdev = inode->i_cdev; struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev); @@ -309,7 +309,8 @@ static int __init bsr_init(void) goto out_err_2; } - if ((ret = bsr_create_devs(np)) < 0) { + ret = bsr_create_devs(np); + if (ret < 0) { np = NULL; goto out_err_3; } diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 3a1e6b3ccd10..5c39f20378b8 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Intel & MS High Precision Event Timer Implementation. * @@ -5,10 +6,6 @@ * Venki Pallipadi * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. * Bob Picco <robert.picco@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/interrupt.h> diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 95be7228f327..9044d31ab1a1 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -4,7 +4,7 @@ * Copyright 2006 Michael Buesch <m@bues.ch> * Copyright 2005 (c) MontaVista Software, Inc. * - * Please read Documentation/hw_random.txt for details on use. + * Please read Documentation/admin-guide/hw_random.rst for details on use. * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. diff --git a/drivers/char/hw_random/hisi-rng.c b/drivers/char/hw_random/hisi-rng.c index 40d96572c591..c663d5dd85bb 100644 --- a/drivers/char/hw_random/hisi-rng.c +++ b/drivers/char/hw_random/hisi-rng.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 HiSilicon Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/err.h> diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c index 8b5a20b35293..92be1c0ab99f 100644 --- a/drivers/char/hw_random/iproc-rng200.c +++ b/drivers/char/hw_random/iproc-rng200.c @@ -220,6 +220,7 @@ static int iproc_rng200_probe(struct platform_device *pdev) } static const struct of_device_id iproc_rng200_of_match[] = { + { .compatible = "brcm,bcm7211-rng200", }, { .compatible = "brcm,bcm7278-rng200", }, { .compatible = "brcm,iproc-rng200", }, {}, diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c index 2e23be802a62..76e693da5dde 100644 --- a/drivers/char/hw_random/meson-rng.c +++ b/drivers/char/hw_random/meson-rng.c @@ -1,58 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong <narmstrong@baylibre.com> * Copyright (C) 2014 Amlogic, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong <narmstrong@baylibre.com> - * Copyright (C) 2014 Amlogic, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <linux/err.h> #include <linux/module.h> diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c index 938ec10e733d..bd6a98b3479b 100644 --- a/drivers/char/hw_random/st-rng.c +++ b/drivers/char/hw_random/st-rng.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ST Random Number Generator Driver ST's Platforms * @@ -5,10 +6,6 @@ * Lee Jones <lee.jones@linaro.org> * * Copyright (C) 2015 STMicroelectronics (R&D) Limited - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/clk.h> diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index f615684028af..ccd1f6e0696b 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/char/hw_random/timeriomem-rng.c * @@ -7,10 +8,6 @@ * Copyright 2005 (c) MontaVista Software, Inc. * Author: Deepak Saxena <dsaxena@plexity.net> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Overview: * This driver is useful for platforms that have an IO range that provides * periodic random data from a single IO memory address. All the platform diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index caac5d24baa4..4bad0614109b 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -132,3 +132,12 @@ config ASPEED_BT_IPMI_BMC Provides a driver for the BT (Block Transfer) IPMI interface found on Aspeed SOCs (AST2400 and AST2500). The driver implements the BMC side of the BT interface. + +config IPMB_DEVICE_INTERFACE + tristate 'IPMB Interface handler' + depends on I2C + depends on I2C_SLAVE + help + Provides a driver for a device (Satellite MC) to + receive requests and send responses back to the BMC via + the IPMB interface. This module requires I2C support. diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 3f06b2062475..0822adc2ec41 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o +obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c new file mode 100644 index 000000000000..57204335c5f5 --- /dev/null +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * IPMB driver to receive a request and send a response + * + * Copyright (C) 2019 Mellanox Techologies, Ltd. + * + * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver. + */ + +#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/wait.h> + +#define MAX_MSG_LEN 128 +#define IPMB_REQUEST_LEN_MIN 7 +#define NETFN_RSP_BIT_MASK 0x4 +#define REQUEST_QUEUE_MAX_LEN 256 + +#define IPMB_MSG_LEN_IDX 0 +#define RQ_SA_8BIT_IDX 1 +#define NETFN_LUN_IDX 2 + +#define GET_7BIT_ADDR(addr_8bit) (addr_8bit >> 1) +#define GET_8BIT_ADDR(addr_7bit) ((addr_7bit << 1) & 0xff) + +#define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1) + +#define SMBUS_MSG_HEADER_LENGTH 2 +#define SMBUS_MSG_IDX_OFFSET (SMBUS_MSG_HEADER_LENGTH + 1) + +struct ipmb_msg { + u8 len; + u8 rs_sa; + u8 netfn_rs_lun; + u8 checksum1; + u8 rq_sa; + u8 rq_seq_rq_lun; + u8 cmd; + u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX]; + /* checksum2 is included in payload */ +} __packed; + +struct ipmb_request_elem { + struct list_head list; + struct ipmb_msg request; +}; + +struct ipmb_dev { + struct i2c_client *client; + struct miscdevice miscdev; + struct ipmb_msg request; + struct list_head request_queue; + atomic_t request_queue_len; + size_t msg_idx; + spinlock_t lock; + wait_queue_head_t wait_queue; + struct mutex file_mutex; +}; + +static inline struct ipmb_dev *to_ipmb_dev(struct file *file) +{ + return container_of(file->private_data, struct ipmb_dev, miscdev); +} + +static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + struct ipmb_request_elem *queue_elem; + struct ipmb_msg msg; + ssize_t ret; + + memset(&msg, 0, sizeof(msg)); + + spin_lock_irq(&ipmb_dev->lock); + + while (list_empty(&ipmb_dev->request_queue)) { + spin_unlock_irq(&ipmb_dev->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(ipmb_dev->wait_queue, + !list_empty(&ipmb_dev->request_queue)); + if (ret) + return ret; + + spin_lock_irq(&ipmb_dev->lock); + } + + queue_elem = list_first_entry(&ipmb_dev->request_queue, + struct ipmb_request_elem, list); + memcpy(&msg, &queue_elem->request, sizeof(msg)); + list_del(&queue_elem->list); + kfree(queue_elem); + atomic_dec(&ipmb_dev->request_queue_len); + + spin_unlock_irq(&ipmb_dev->lock); + + count = min_t(size_t, count, msg.len + 1); + if (copy_to_user(buf, &msg, count)) + ret = -EFAULT; + + return ret < 0 ? ret : count; +} + +static ssize_t ipmb_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + u8 rq_sa, netf_rq_lun, msg_len; + union i2c_smbus_data data; + u8 msg[MAX_MSG_LEN]; + ssize_t ret; + + if (count > sizeof(msg)) + return -EINVAL; + + if (copy_from_user(&msg, buf, count)) + return -EFAULT; + + if (count < msg[0]) + return -EINVAL; + + rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); + netf_rq_lun = msg[NETFN_LUN_IDX]; + + if (!(netf_rq_lun & NETFN_RSP_BIT_MASK)) + return -EINVAL; + + /* + * subtract rq_sa and netf_rq_lun from the length of the msg passed to + * i2c_smbus_xfer + */ + msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH; + if (msg_len > I2C_SMBUS_BLOCK_MAX) + msg_len = I2C_SMBUS_BLOCK_MAX; + + data.block[0] = msg_len; + memcpy(&data.block[1], msg + SMBUS_MSG_IDX_OFFSET, msg_len); + ret = i2c_smbus_xfer(ipmb_dev->client->adapter, rq_sa, + ipmb_dev->client->flags, + I2C_SMBUS_WRITE, netf_rq_lun, + I2C_SMBUS_BLOCK_DATA, &data); + + return ret ? : count; +} + +static unsigned int ipmb_poll(struct file *file, poll_table *wait) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + unsigned int mask = POLLOUT; + + mutex_lock(&ipmb_dev->file_mutex); + poll_wait(file, &ipmb_dev->wait_queue, wait); + + if (atomic_read(&ipmb_dev->request_queue_len)) + mask |= POLLIN; + mutex_unlock(&ipmb_dev->file_mutex); + + return mask; +} + +static const struct file_operations ipmb_fops = { + .owner = THIS_MODULE, + .read = ipmb_read, + .write = ipmb_write, + .poll = ipmb_poll, +}; + +/* Called with ipmb_dev->lock held. */ +static void ipmb_handle_request(struct ipmb_dev *ipmb_dev) +{ + struct ipmb_request_elem *queue_elem; + + if (atomic_read(&ipmb_dev->request_queue_len) >= + REQUEST_QUEUE_MAX_LEN) + return; + + queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC); + if (!queue_elem) + return; + + memcpy(&queue_elem->request, &ipmb_dev->request, + sizeof(struct ipmb_msg)); + list_add(&queue_elem->list, &ipmb_dev->request_queue); + atomic_inc(&ipmb_dev->request_queue_len); + wake_up_all(&ipmb_dev->wait_queue); +} + +static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + /* The 8 lsb of the sum is 0 when the checksum is valid */ + return (rs_sa + ipmb_dev->request.netfn_rs_lun + + ipmb_dev->request.checksum1); +} + +static bool is_ipmb_request(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + if (ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) { + if (ipmb_verify_checksum1(ipmb_dev, rs_sa)) + return false; + + /* + * Check whether this is an IPMB request or + * response. + * The 6 MSB of netfn_rs_lun are dedicated to the netfn + * while the remaining bits are dedicated to the lun. + * If the LSB of the netfn is cleared, it is associated + * with an IPMB request. + * If the LSB of the netfn is set, it is associated with + * an IPMB response. + */ + if (!(ipmb_dev->request.netfn_rs_lun & NETFN_RSP_BIT_MASK)) + return true; + } + return false; +} + +/* + * The IPMB protocol only supports I2C Writes so there is no need + * to support I2C_SLAVE_READ* events. + * This i2c callback function only monitors IPMB request messages + * and adds them in a queue, so that they can be handled by + * receive_ipmb_request. + */ +static int ipmb_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + u8 *buf = (u8 *)&ipmb_dev->request; + unsigned long flags; + + spin_lock_irqsave(&ipmb_dev->lock, flags); + switch (event) { + case I2C_SLAVE_WRITE_REQUESTED: + memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); + ipmb_dev->msg_idx = 0; + + /* + * At index 0, ipmb_msg stores the length of msg, + * skip it for now. + * The len will be populated once the whole + * buf is populated. + * + * The I2C bus driver's responsibility is to pass the + * data bytes to the backend driver; it does not + * forward the i2c slave address. + * Since the first byte in the IPMB message is the + * address of the responder, it is the responsibility + * of the IPMB driver to format the message properly. + * So this driver prepends the address of the responder + * to the received i2c data before the request message + * is handled in userland. + */ + buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr); + break; + + case I2C_SLAVE_WRITE_RECEIVED: + if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg)) + break; + + buf[++ipmb_dev->msg_idx] = *val; + break; + + case I2C_SLAVE_STOP: + ipmb_dev->request.len = ipmb_dev->msg_idx; + + if (is_ipmb_request(ipmb_dev, GET_8BIT_ADDR(client->addr))) + ipmb_handle_request(ipmb_dev); + break; + + default: + break; + } + spin_unlock_irqrestore(&ipmb_dev->lock, flags); + + return 0; +} + +static int ipmb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ipmb_dev *ipmb_dev; + int ret; + + ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev), + GFP_KERNEL); + if (!ipmb_dev) + return -ENOMEM; + + spin_lock_init(&ipmb_dev->lock); + init_waitqueue_head(&ipmb_dev->wait_queue); + atomic_set(&ipmb_dev->request_queue_len, 0); + INIT_LIST_HEAD(&ipmb_dev->request_queue); + + mutex_init(&ipmb_dev->file_mutex); + + ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + + ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL, + "%s%d", "ipmb-", + client->adapter->nr); + ipmb_dev->miscdev.fops = &ipmb_fops; + ipmb_dev->miscdev.parent = &client->dev; + ret = misc_register(&ipmb_dev->miscdev); + if (ret) + return ret; + + ipmb_dev->client = client; + i2c_set_clientdata(client, ipmb_dev); + ret = i2c_slave_register(client, ipmb_slave_cb); + if (ret) { + misc_deregister(&ipmb_dev->miscdev); + return ret; + } + + return 0; +} + +static int ipmb_remove(struct i2c_client *client) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + + i2c_slave_unregister(client); + misc_deregister(&ipmb_dev->miscdev); + + return 0; +} + +static const struct i2c_device_id ipmb_id[] = { + { "ipmb-dev", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ipmb_id); + +static const struct acpi_device_id acpi_ipmb_id[] = { + { "IPMB0001", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); + +static struct i2c_driver ipmb_driver = { + .driver = { + .name = "ipmb-dev", + .acpi_match_table = ACPI_PTR(acpi_ipmb_id), + }, + .probe = ipmb_probe, + .remove = ipmb_remove, + .id_table = ipmb_id, +}; +module_i2c_driver(ipmb_driver); + +MODULE_AUTHOR("Mellanox Technologies"); +MODULE_DESCRIPTION("IPMB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 1dc10740fc0f..6707659cffd6 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2819,9 +2819,9 @@ static const struct device_type bmc_device_type = { .groups = bmc_dev_attr_groups, }; -static int __find_bmc_guid(struct device *dev, void *data) +static int __find_bmc_guid(struct device *dev, const void *data) { - guid_t *guid = data; + const guid_t *guid = data; struct bmc_device *bmc; int rv; @@ -2857,9 +2857,9 @@ struct prod_dev_id { unsigned char device_id; }; -static int __find_bmc_prod_dev_id(struct device *dev, void *data) +static int __find_bmc_prod_dev_id(struct device *dev, const void *data) { - struct prod_dev_id *cid = data; + const struct prod_dev_id *cid = data; struct bmc_device *bmc; int rv; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f124a2d2bb9f..da5b6723329a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -71,7 +71,7 @@ enum si_intf_state { static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" }; -static int initialized; +static bool initialized; /* * Indexes into stats[] in smi_info below. @@ -2124,7 +2124,7 @@ static int __init init_ipmi_si(void) } skip_fallback_noirq: - initialized = 1; + initialized = true; mutex_unlock(&smi_infos_lock); if (type) diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index f2a91c4d8cab..22f6c9b20e9a 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -19,6 +19,7 @@ #include "ipmi_si.h" #include "ipmi_dmi.h" +static bool platform_registered; static bool si_tryplatform = true; #ifdef CONFIG_ACPI static bool si_tryacpi = true; @@ -426,7 +427,7 @@ static int ipmi_remove(struct platform_device *pdev) return ipmi_si_remove_by_dev(&pdev->dev); } -static int pdev_match_name(struct device *dev, void *data) +static int pdev_match_name(struct device *dev, const void *data) { struct platform_device *pdev = to_platform_device(dev); const char *name = data; @@ -443,6 +444,7 @@ void ipmi_remove_platform_device_by_name(char *name) struct platform_device *pdev = to_platform_device(dev); platform_device_unregister(pdev); + put_device(dev); } } @@ -469,9 +471,12 @@ void ipmi_si_platform_init(void) int rv = platform_driver_register(&ipmi_platform_driver); if (rv) pr_err("Unable to register driver: %d\n", rv); + else + platform_registered = true; } void ipmi_si_platform_shutdown(void) { - platform_driver_unregister(&ipmi_platform_driver); + if (platform_registered) + platform_driver_unregister(&ipmi_platform_driver); } diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index cf8156d6bc07..305fa5054274 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -303,6 +303,7 @@ struct ssif_info { ((unsigned int) atomic_read(&(ssif)->stats[SSIF_STAT_ ## stat])) static bool initialized; +static bool platform_registered; static void return_hosed_msg(struct ssif_info *ssif_info, struct ipmi_smi_msg *msg); @@ -2088,6 +2089,8 @@ static int init_ipmi_ssif(void) rv = platform_driver_register(&ipmi_driver); if (rv) pr_err("Unable to register driver: %d\n", rv); + else + platform_registered = true; } ssif_i2c_driver.address_list = ssif_address_list(); @@ -2111,7 +2114,7 @@ static void cleanup_ipmi_ssif(void) kfree(ssif_i2c_driver.address_list); - if (ssif_trydmi) + if (ssif_trydmi && platform_registered) platform_driver_unregister(&ipmi_driver); free_ssif_clients(); diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 53cfe574d8d4..f6a147427029 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -226,6 +226,7 @@ int misc_register(struct miscdevice *misc) mutex_unlock(&misc_mtx); return err; } +EXPORT_SYMBOL(misc_register); /** * misc_deregister - unregister a miscellaneous device @@ -249,8 +250,6 @@ void misc_deregister(struct miscdevice *misc) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); } - -EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); static char *misc_devnode(struct device *dev, umode_t *mode) diff --git a/drivers/char/tpm/eventlog/efi.c b/drivers/char/tpm/eventlog/efi.c index 3e44362e469c..6bb023de17f1 100644 --- a/drivers/char/tpm/eventlog/efi.c +++ b/drivers/char/tpm/eventlog/efi.c @@ -16,10 +16,13 @@ int tpm_read_log_efi(struct tpm_chip *chip) { + struct efi_tcg2_final_events_table *final_tbl = NULL; struct linux_efi_tpm_eventlog *log_tbl; struct tpm_bios_log *log; u32 log_size; u8 tpm_log_version; + void *tmp; + int ret; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) return -ENODEV; @@ -47,15 +50,57 @@ int tpm_read_log_efi(struct tpm_chip *chip) /* malloc EventLog space */ log->bios_event_log = kmemdup(log_tbl->log, log_size, GFP_KERNEL); - if (!log->bios_event_log) - goto err_memunmap; - log->bios_event_log_end = log->bios_event_log + log_size; + if (!log->bios_event_log) { + ret = -ENOMEM; + goto out; + } + log->bios_event_log_end = log->bios_event_log + log_size; tpm_log_version = log_tbl->version; - memunmap(log_tbl); - return tpm_log_version; -err_memunmap: + ret = tpm_log_version; + + if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR || + efi_tpm_final_log_size == 0 || + tpm_log_version != EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) + goto out; + + final_tbl = memremap(efi.tpm_final_log, + sizeof(*final_tbl) + efi_tpm_final_log_size, + MEMREMAP_WB); + if (!final_tbl) { + pr_err("Could not map UEFI TPM final log\n"); + kfree(log->bios_event_log); + ret = -ENOMEM; + goto out; + } + + efi_tpm_final_log_size -= log_tbl->final_events_preboot_size; + + tmp = krealloc(log->bios_event_log, + log_size + efi_tpm_final_log_size, + GFP_KERNEL); + if (!tmp) { + kfree(log->bios_event_log); + ret = -ENOMEM; + goto out; + } + + log->bios_event_log = tmp; + + /* + * Copy any of the final events log that didn't also end up in the + * main log. Events can be logged in both if events are generated + * between GetEventLog() and ExitBootServices(). + */ + memcpy((void *)log->bios_event_log + log_size, + final_tbl->events + log_tbl->final_events_preboot_size, + efi_tpm_final_log_size); + log->bios_event_log_end = log->bios_event_log + + log_size + efi_tpm_final_log_size; + +out: + memunmap(final_tbl); memunmap(log_tbl); - return -ENOMEM; + return ret; } diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c index d506362e046f..b9aeda1cbcd7 100644 --- a/drivers/char/tpm/eventlog/tpm2.c +++ b/drivers/char/tpm/eventlog/tpm2.c @@ -36,52 +36,7 @@ static size_t calc_tpm2_event_size(struct tcg_pcr_event2_head *event, struct tcg_pcr_event *event_header) { - struct tcg_efi_specid_event_head *efispecid; - struct tcg_event_field *event_field; - void *marker; - void *marker_start; - u32 halg_size; - size_t size; - u16 halg; - int i; - int j; - - marker = event; - marker_start = marker; - marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type) - + sizeof(event->count); - - efispecid = (struct tcg_efi_specid_event_head *)event_header->event; - - /* Check if event is malformed. */ - if (event->count > efispecid->num_algs) - return 0; - - for (i = 0; i < event->count; i++) { - halg_size = sizeof(event->digests[i].alg_id); - memcpy(&halg, marker, halg_size); - marker = marker + halg_size; - for (j = 0; j < efispecid->num_algs; j++) { - if (halg == efispecid->digest_sizes[j].alg_id) { - marker += - efispecid->digest_sizes[j].digest_size; - break; - } - } - /* Algorithm without known length. Such event is unparseable. */ - if (j == efispecid->num_algs) - return 0; - } - - event_field = (struct tcg_event_field *)marker; - marker = marker + sizeof(event_field->event_size) - + event_field->event_size; - size = marker - marker_start; - - if ((event->event_type == 0) && (event_field->event_size == 0)) - return 0; - - return size; + return __calc_tpm2_event_size(event, event_header, false); } static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 90325e1749fb..d47ad10a35fe 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -289,15 +289,15 @@ static int tpm_class_shutdown(struct device *dev) { struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); + down_write(&chip->ops_sem); if (chip->flags & TPM_CHIP_FLAG_TPM2) { - down_write(&chip->ops_sem); if (!tpm_chip_start(chip)) { tpm2_shutdown(chip, TPM2_SU_CLEAR); tpm_chip_stop(chip); } - chip->ops = NULL; - up_write(&chip->ops_sem); } + chip->ops = NULL; + up_write(&chip->ops_sem); return 0; } diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 85dcf2654d11..faacbe1ffa1a 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -510,7 +510,7 @@ struct tpm1_get_random_out { * * Return: * * number of bytes read - * * -errno or a TPM return code otherwise + * * -errno (positive TPM return codes are masked to -EIO) */ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) { @@ -531,8 +531,11 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len), "attempting get random"); - if (rc) + if (rc) { + if (rc > 0) + rc = -EIO; goto out; + } out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE]; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 4de49924cfc4..d103545e4055 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -297,7 +297,7 @@ struct tpm2_get_random_out { * * Return: * size of the buffer on success, - * -errno otherwise + * -errno otherwise (positive TPM return codes are masked to -EIO) */ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) { @@ -324,8 +324,11 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) offsetof(struct tpm2_get_random_out, buffer), "attempting get random"); - if (err) + if (err) { + if (err > 0) + err = -EIO; goto out; + } out = (struct tpm2_get_random_out *) &buf.data[TPM_HEADER_SIZE]; diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index 0c751a79bbed..7a0a7051a06f 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -1,7 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 James.Bottomley@HansenPartnership.com - * - * GPLv2 */ #include <linux/slab.h> #include "tpm-dev.h" diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 4e2d00cb0d81..da5b30771418 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Implementation of the Xen vTPM device frontend * * Author: Daniel De Graaf <dgdegra@tycho.nsa.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. */ #include <linux/errno.h> #include <linux/err.h> |