2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/arm/common/amba.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
2005-10-31 07:03:48 +08:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/slab.h>
|
2009-01-08 17:58:51 +08:00
|
|
|
#include <linux/io.h>
|
2011-02-23 11:33:17 +08:00
|
|
|
#include <linux/pm.h>
|
|
|
|
#include <linux/pm_runtime.h>
|
2014-09-29 19:58:47 +08:00
|
|
|
#include <linux/pm_domain.h>
|
2006-01-07 21:52:45 +08:00
|
|
|
#include <linux/amba/bus.h>
|
2012-06-24 19:46:16 +08:00
|
|
|
#include <linux/sizes.h>
|
2015-01-06 18:15:11 +08:00
|
|
|
#include <linux/limits.h>
|
2016-08-11 03:17:34 +08:00
|
|
|
#include <linux/clk/clk-conf.h>
|
2018-04-28 10:51:58 +08:00
|
|
|
#include <linux/platform_device.h>
|
2023-10-06 22:57:31 +08:00
|
|
|
#include <linux/property.h>
|
2019-09-04 09:13:08 +08:00
|
|
|
#include <linux/reset.h>
|
2021-08-23 17:41:43 +08:00
|
|
|
#include <linux/of_irq.h>
|
2022-04-18 08:49:52 +08:00
|
|
|
#include <linux/of_device.h>
|
|
|
|
#include <linux/acpi.h>
|
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers
with the device DMA managed by kernel drivers or user-space applications.
Unfortunately, multiple devices may be placed in the same IOMMU group
because they cannot be isolated from each other. The DMA on these devices
must either be entirely under kernel control or userspace control, never
a mixture. Otherwise the driver integrity is not guaranteed because they
could access each other through the peer-to-peer accesses which by-pass
the IOMMU protection.
This checks and sets the default DMA mode during driver binding, and
cleanups during driver unbinding. In the default mode, the device DMA is
managed by the device driver which handles DMA operations through the
kernel DMA APIs (see Documentation/core-api/dma-api.rst).
For cases where the devices are assigned for userspace control through the
userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/
vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this
default setting in the assumption that the drivers know what they are
doing with the device DMA.
Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure
is currently a problem. As things stand, the IOMMU driver ignored the
initial iommu_probe_device() call when the device was added, since at
that point it had no fwspec yet. In this situation,
{of,acpi}_iommu_configure() are retriggering iommu_probe_device() after
the IOMMU driver has seen the firmware data via .of_xlate to learn that
it actually responsible for the given device. As the result, before
that gets fixed, iommu_use_default_domain() goes at the end, and calls
arch_teardown_dma_ops() if it fails.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Stuart Yoder <stuyoder@gmail.com>
Cc: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20220418005000.897664-5-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2022-04-18 08:49:53 +08:00
|
|
|
#include <linux/iommu.h>
|
|
|
|
#include <linux/dma-map-ops.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
|
|
|
|
|
2019-02-13 21:41:50 +08:00
|
|
|
/* called on periphid match and class 0x9 coresight device. */
|
|
|
|
static int
|
|
|
|
amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2019-02-13 21:41:50 +08:00
|
|
|
struct amba_cs_uci_id *uci;
|
|
|
|
|
|
|
|
uci = table->data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-13 21:41:50 +08:00
|
|
|
/* no table data or zero mask - return match on periphid */
|
|
|
|
if (!uci || (uci->devarch_mask == 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* test against read devtype and masked devarch value */
|
|
|
|
ret = (dev->uci.devtype == uci->devtype) &&
|
|
|
|
((dev->uci.devarch & uci->devarch_mask) == uci->devarch);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct amba_id *
|
|
|
|
amba_lookup(const struct amba_id *table, struct amba_device *dev)
|
|
|
|
{
|
2005-04-17 06:20:36 +08:00
|
|
|
while (table->mask) {
|
2019-02-13 21:41:50 +08:00
|
|
|
if (((dev->periphid & table->mask) == table->id) &&
|
|
|
|
((dev->cid != CORESIGHT_CID) ||
|
|
|
|
(amba_cs_uci_id_match(table, dev))))
|
|
|
|
return table;
|
2005-04-17 06:20:36 +08:00
|
|
|
table++;
|
|
|
|
}
|
2019-02-13 21:41:50 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
static int amba_get_enable_pclk(struct amba_device *pcdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-01-27 00:58:32 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk");
|
|
|
|
if (IS_ERR(pcdev->pclk))
|
|
|
|
return PTR_ERR(pcdev->pclk);
|
2015-01-06 18:15:11 +08:00
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
ret = clk_prepare_enable(pcdev->pclk);
|
|
|
|
if (ret)
|
|
|
|
clk_put(pcdev->pclk);
|
|
|
|
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
static void amba_put_disable_pclk(struct amba_device *pcdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-01-27 00:58:32 +08:00
|
|
|
clk_disable_unprepare(pcdev->pclk);
|
|
|
|
clk_put(pcdev->pclk);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
|
2015-01-06 18:15:11 +08:00
|
|
|
static ssize_t driver_override_show(struct device *_dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct amba_device *dev = to_amba_device(_dev);
|
2018-04-10 21:21:44 +08:00
|
|
|
ssize_t len;
|
2015-01-06 18:15:11 +08:00
|
|
|
|
2018-04-10 21:21:44 +08:00
|
|
|
device_lock(_dev);
|
|
|
|
len = sprintf(buf, "%s\n", dev->driver_override);
|
|
|
|
device_unlock(_dev);
|
|
|
|
return len;
|
2015-01-06 18:15:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t driver_override_store(struct device *_dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct amba_device *dev = to_amba_device(_dev);
|
2022-04-19 19:34:25 +08:00
|
|
|
int ret;
|
2015-01-06 18:15:11 +08:00
|
|
|
|
2022-04-19 19:34:25 +08:00
|
|
|
ret = driver_set_override(_dev, &dev->driver_override, buf, count);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2015-01-06 18:15:11 +08:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
2017-06-06 20:16:49 +08:00
|
|
|
static DEVICE_ATTR_RW(driver_override);
|
2015-01-06 18:15:11 +08:00
|
|
|
|
2006-11-30 22:04:49 +08:00
|
|
|
#define amba_attr_func(name,fmt,arg...) \
|
|
|
|
static ssize_t name##_show(struct device *_dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
struct amba_device *dev = to_amba_device(_dev); \
|
|
|
|
return sprintf(buf, fmt, arg); \
|
2017-06-06 20:16:49 +08:00
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(name)
|
2006-11-30 22:04:49 +08:00
|
|
|
|
|
|
|
amba_attr_func(id, "%08x\n", dev->periphid);
|
|
|
|
amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n",
|
|
|
|
(unsigned long long)dev->res.start, (unsigned long long)dev->res.end,
|
|
|
|
dev->res.flags);
|
|
|
|
|
2017-06-06 20:16:49 +08:00
|
|
|
static struct attribute *amba_dev_attrs[] = {
|
|
|
|
&dev_attr_id.attr,
|
|
|
|
&dev_attr_resource.attr,
|
|
|
|
&dev_attr_driver_override.attr,
|
|
|
|
NULL,
|
2006-11-30 22:04:49 +08:00
|
|
|
};
|
2017-06-06 20:16:49 +08:00
|
|
|
ATTRIBUTE_GROUPS(amba_dev);
|
2006-11-30 22:04:49 +08:00
|
|
|
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
static int amba_read_periphid(struct amba_device *dev)
|
|
|
|
{
|
|
|
|
struct reset_control *rstc;
|
|
|
|
u32 size, pid, cid;
|
|
|
|
void __iomem *tmp;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
ret = dev_pm_domain_attach(&dev->dev, true);
|
|
|
|
if (ret) {
|
|
|
|
dev_dbg(&dev->dev, "can't get PM domain: %d\n", ret);
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = amba_get_enable_pclk(dev);
|
|
|
|
if (ret) {
|
|
|
|
dev_dbg(&dev->dev, "can't get pclk: %d\n", ret);
|
|
|
|
goto err_pm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find reset control(s) of the amba bus and de-assert them.
|
|
|
|
*/
|
|
|
|
rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node);
|
|
|
|
if (IS_ERR(rstc)) {
|
|
|
|
ret = PTR_ERR(rstc);
|
|
|
|
if (ret != -EPROBE_DEFER)
|
|
|
|
dev_err(&dev->dev, "can't get reset: %d\n", ret);
|
|
|
|
goto err_clk;
|
|
|
|
}
|
|
|
|
reset_control_deassert(rstc);
|
|
|
|
reset_control_put(rstc);
|
|
|
|
|
|
|
|
size = resource_size(&dev->res);
|
|
|
|
tmp = ioremap(dev->res.start, size);
|
|
|
|
if (!tmp) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_clk;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read pid and cid based on size of resource
|
|
|
|
* they are located at end of region
|
|
|
|
*/
|
|
|
|
for (pid = 0, i = 0; i < 4; i++)
|
|
|
|
pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8);
|
|
|
|
for (cid = 0, i = 0; i < 4; i++)
|
|
|
|
cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
|
|
|
|
|
|
|
|
if (cid == CORESIGHT_CID) {
|
|
|
|
/* set the base to the start of the last 4k block */
|
|
|
|
void __iomem *csbase = tmp + size - 4096;
|
|
|
|
|
|
|
|
dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET);
|
|
|
|
dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cid == AMBA_CID || cid == CORESIGHT_CID) {
|
|
|
|
dev->periphid = pid;
|
|
|
|
dev->cid = cid;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev->periphid)
|
|
|
|
ret = -ENODEV;
|
|
|
|
|
|
|
|
iounmap(tmp);
|
|
|
|
|
|
|
|
err_clk:
|
|
|
|
amba_put_disable_pclk(dev);
|
|
|
|
err_pm:
|
|
|
|
dev_pm_domain_detach(&dev->dev, true);
|
|
|
|
err_out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
static int amba_match(struct device *dev, struct device_driver *drv)
|
|
|
|
{
|
|
|
|
struct amba_device *pcdev = to_amba_device(dev);
|
|
|
|
struct amba_driver *pcdrv = to_amba_driver(drv);
|
|
|
|
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
mutex_lock(&pcdev->periphid_lock);
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
if (!pcdev->periphid) {
|
|
|
|
int ret = amba_read_periphid(pcdev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returning any error other than -EPROBE_DEFER from bus match
|
|
|
|
* can cause driver registration failure. So, if there's a
|
|
|
|
* permanent failure in reading pid and cid, simply map it to
|
|
|
|
* -EPROBE_DEFER.
|
|
|
|
*/
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
if (ret) {
|
|
|
|
mutex_unlock(&pcdev->periphid_lock);
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
return -EPROBE_DEFER;
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
}
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
dev_set_uevent_suppress(dev, false);
|
|
|
|
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
|
|
|
}
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
mutex_unlock(&pcdev->periphid_lock);
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
|
2021-01-27 00:58:32 +08:00
|
|
|
/* When driver_override is set, only bind to the matching driver */
|
|
|
|
if (pcdev->driver_override)
|
|
|
|
return !strcmp(pcdev->driver_override, drv->name);
|
|
|
|
|
|
|
|
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
|
|
|
|
}
|
|
|
|
|
2023-01-11 19:30:17 +08:00
|
|
|
static int amba_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
2021-01-27 00:58:32 +08:00
|
|
|
{
|
2023-01-11 19:30:17 +08:00
|
|
|
const struct amba_device *pcdev = to_amba_device(dev);
|
2021-01-27 00:58:32 +08:00
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2021-12-03 17:25:21 +08:00
|
|
|
static int of_amba_device_decode_irq(struct amba_device *dev)
|
|
|
|
{
|
|
|
|
struct device_node *node = dev->dev.of_node;
|
|
|
|
int i, irq = 0;
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_OF_IRQ) && node) {
|
|
|
|
/* Decode the IRQs and address ranges */
|
|
|
|
for (i = 0; i < AMBA_NR_IRQS; i++) {
|
|
|
|
irq = of_irq_get(node, i);
|
|
|
|
if (irq < 0) {
|
|
|
|
if (irq == -EPROBE_DEFER)
|
|
|
|
return irq;
|
|
|
|
irq = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->irq[i] = irq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-27 00:58:35 +08:00
|
|
|
/*
|
|
|
|
* These are the device model conversion veneers; they convert the
|
|
|
|
* device model structures to our more specific structures.
|
|
|
|
*/
|
|
|
|
static int amba_probe(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_device *pcdev = to_amba_device(dev);
|
|
|
|
struct amba_driver *pcdrv = to_amba_driver(dev->driver);
|
|
|
|
const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
do {
|
2021-12-03 17:25:21 +08:00
|
|
|
ret = of_amba_device_decode_irq(pcdev);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
2021-01-27 00:58:35 +08:00
|
|
|
ret = of_clk_set_defaults(dev->of_node, false);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = dev_pm_domain_attach(dev, true);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = amba_get_enable_pclk(pcdev);
|
|
|
|
if (ret) {
|
|
|
|
dev_pm_domain_detach(dev, true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pm_runtime_get_noresume(dev);
|
|
|
|
pm_runtime_set_active(dev);
|
|
|
|
pm_runtime_enable(dev);
|
|
|
|
|
|
|
|
ret = pcdrv->probe(pcdev, id);
|
|
|
|
if (ret == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
pm_runtime_disable(dev);
|
|
|
|
pm_runtime_set_suspended(dev);
|
|
|
|
pm_runtime_put_noidle(dev);
|
|
|
|
|
|
|
|
amba_put_disable_pclk(pcdev);
|
|
|
|
dev_pm_domain_detach(dev, true);
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-07-14 03:35:22 +08:00
|
|
|
static void amba_remove(struct device *dev)
|
2021-01-27 00:58:35 +08:00
|
|
|
{
|
|
|
|
struct amba_device *pcdev = to_amba_device(dev);
|
|
|
|
struct amba_driver *drv = to_amba_driver(dev->driver);
|
|
|
|
|
|
|
|
pm_runtime_get_sync(dev);
|
|
|
|
if (drv->remove)
|
|
|
|
drv->remove(pcdev);
|
|
|
|
pm_runtime_put_noidle(dev);
|
|
|
|
|
|
|
|
/* Undo the runtime PM settings in amba_probe() */
|
|
|
|
pm_runtime_disable(dev);
|
|
|
|
pm_runtime_set_suspended(dev);
|
|
|
|
pm_runtime_put_noidle(dev);
|
|
|
|
|
|
|
|
amba_put_disable_pclk(pcdev);
|
|
|
|
dev_pm_domain_detach(dev, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void amba_shutdown(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_driver *drv;
|
|
|
|
|
|
|
|
if (!dev->driver)
|
|
|
|
return;
|
|
|
|
|
|
|
|
drv = to_amba_driver(dev->driver);
|
|
|
|
if (drv->shutdown)
|
|
|
|
drv->shutdown(to_amba_device(dev));
|
|
|
|
}
|
|
|
|
|
2022-04-18 08:49:52 +08:00
|
|
|
static int amba_dma_configure(struct device *dev)
|
|
|
|
{
|
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers
with the device DMA managed by kernel drivers or user-space applications.
Unfortunately, multiple devices may be placed in the same IOMMU group
because they cannot be isolated from each other. The DMA on these devices
must either be entirely under kernel control or userspace control, never
a mixture. Otherwise the driver integrity is not guaranteed because they
could access each other through the peer-to-peer accesses which by-pass
the IOMMU protection.
This checks and sets the default DMA mode during driver binding, and
cleanups during driver unbinding. In the default mode, the device DMA is
managed by the device driver which handles DMA operations through the
kernel DMA APIs (see Documentation/core-api/dma-api.rst).
For cases where the devices are assigned for userspace control through the
userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/
vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this
default setting in the assumption that the drivers know what they are
doing with the device DMA.
Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure
is currently a problem. As things stand, the IOMMU driver ignored the
initial iommu_probe_device() call when the device was added, since at
that point it had no fwspec yet. In this situation,
{of,acpi}_iommu_configure() are retriggering iommu_probe_device() after
the IOMMU driver has seen the firmware data via .of_xlate to learn that
it actually responsible for the given device. As the result, before
that gets fixed, iommu_use_default_domain() goes at the end, and calls
arch_teardown_dma_ops() if it fails.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Stuart Yoder <stuyoder@gmail.com>
Cc: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20220418005000.897664-5-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2022-04-18 08:49:53 +08:00
|
|
|
struct amba_driver *drv = to_amba_driver(dev->driver);
|
2022-04-18 08:49:52 +08:00
|
|
|
enum dev_dma_attr attr;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (dev->of_node) {
|
|
|
|
ret = of_dma_configure(dev, dev->of_node, true);
|
|
|
|
} else if (has_acpi_companion(dev)) {
|
|
|
|
attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode));
|
|
|
|
ret = acpi_dma_configure(dev, attr);
|
|
|
|
}
|
|
|
|
|
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers
with the device DMA managed by kernel drivers or user-space applications.
Unfortunately, multiple devices may be placed in the same IOMMU group
because they cannot be isolated from each other. The DMA on these devices
must either be entirely under kernel control or userspace control, never
a mixture. Otherwise the driver integrity is not guaranteed because they
could access each other through the peer-to-peer accesses which by-pass
the IOMMU protection.
This checks and sets the default DMA mode during driver binding, and
cleanups during driver unbinding. In the default mode, the device DMA is
managed by the device driver which handles DMA operations through the
kernel DMA APIs (see Documentation/core-api/dma-api.rst).
For cases where the devices are assigned for userspace control through the
userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/
vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this
default setting in the assumption that the drivers know what they are
doing with the device DMA.
Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure
is currently a problem. As things stand, the IOMMU driver ignored the
initial iommu_probe_device() call when the device was added, since at
that point it had no fwspec yet. In this situation,
{of,acpi}_iommu_configure() are retriggering iommu_probe_device() after
the IOMMU driver has seen the firmware data via .of_xlate to learn that
it actually responsible for the given device. As the result, before
that gets fixed, iommu_use_default_domain() goes at the end, and calls
arch_teardown_dma_ops() if it fails.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Stuart Yoder <stuyoder@gmail.com>
Cc: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20220418005000.897664-5-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2022-04-18 08:49:53 +08:00
|
|
|
if (!ret && !drv->driver_managed_dma) {
|
|
|
|
ret = iommu_device_use_default_domain(dev);
|
|
|
|
if (ret)
|
|
|
|
arch_teardown_dma_ops(dev);
|
|
|
|
}
|
|
|
|
|
2022-04-18 08:49:52 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers
with the device DMA managed by kernel drivers or user-space applications.
Unfortunately, multiple devices may be placed in the same IOMMU group
because they cannot be isolated from each other. The DMA on these devices
must either be entirely under kernel control or userspace control, never
a mixture. Otherwise the driver integrity is not guaranteed because they
could access each other through the peer-to-peer accesses which by-pass
the IOMMU protection.
This checks and sets the default DMA mode during driver binding, and
cleanups during driver unbinding. In the default mode, the device DMA is
managed by the device driver which handles DMA operations through the
kernel DMA APIs (see Documentation/core-api/dma-api.rst).
For cases where the devices are assigned for userspace control through the
userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/
vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this
default setting in the assumption that the drivers know what they are
doing with the device DMA.
Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure
is currently a problem. As things stand, the IOMMU driver ignored the
initial iommu_probe_device() call when the device was added, since at
that point it had no fwspec yet. In this situation,
{of,acpi}_iommu_configure() are retriggering iommu_probe_device() after
the IOMMU driver has seen the firmware data via .of_xlate to learn that
it actually responsible for the given device. As the result, before
that gets fixed, iommu_use_default_domain() goes at the end, and calls
arch_teardown_dma_ops() if it fails.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Stuart Yoder <stuyoder@gmail.com>
Cc: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20220418005000.897664-5-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2022-04-18 08:49:53 +08:00
|
|
|
static void amba_dma_cleanup(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_driver *drv = to_amba_driver(dev->driver);
|
|
|
|
|
|
|
|
if (!drv->driver_managed_dma)
|
|
|
|
iommu_device_unuse_default_domain(dev);
|
|
|
|
}
|
|
|
|
|
2014-02-12 21:06:43 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-08-14 16:13:48 +08:00
|
|
|
/*
|
|
|
|
* Hooks to provide runtime PM of the pclk (bus clock). It is safe to
|
|
|
|
* enable/disable the bus clock at runtime PM suspend/resume as this
|
2012-04-13 20:11:50 +08:00
|
|
|
* does not result in loss of context.
|
2011-08-14 16:13:48 +08:00
|
|
|
*/
|
|
|
|
static int amba_pm_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_device *pcdev = to_amba_device(dev);
|
|
|
|
int ret = pm_generic_runtime_suspend(dev);
|
|
|
|
|
2014-11-14 16:48:27 +08:00
|
|
|
if (ret == 0 && dev->driver) {
|
|
|
|
if (pm_runtime_is_irq_safe(dev))
|
|
|
|
clk_disable(pcdev->pclk);
|
|
|
|
else
|
|
|
|
clk_disable_unprepare(pcdev->pclk);
|
|
|
|
}
|
2011-08-14 16:13:48 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int amba_pm_runtime_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_device *pcdev = to_amba_device(dev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (dev->driver) {
|
2014-11-14 16:48:27 +08:00
|
|
|
if (pm_runtime_is_irq_safe(dev))
|
|
|
|
ret = clk_enable(pcdev->pclk);
|
|
|
|
else
|
|
|
|
ret = clk_prepare_enable(pcdev->pclk);
|
2011-08-14 16:13:48 +08:00
|
|
|
/* Failure is probably fatal to the system, but... */
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pm_generic_runtime_resume(dev);
|
|
|
|
}
|
2014-11-14 16:48:27 +08:00
|
|
|
#endif /* CONFIG_PM */
|
2011-08-14 16:13:48 +08:00
|
|
|
|
2011-02-23 11:33:17 +08:00
|
|
|
static const struct dev_pm_ops amba_pm = {
|
2014-12-04 07:34:11 +08:00
|
|
|
SET_RUNTIME_PM_OPS(
|
2011-08-14 16:13:48 +08:00
|
|
|
amba_pm_runtime_suspend,
|
|
|
|
amba_pm_runtime_resume,
|
PM / Runtime: Rework the "runtime idle" helper routine
The "runtime idle" helper routine, rpm_idle(), currently ignores
return values from .runtime_idle() callbacks executed by it.
However, it turns out that many subsystems use
pm_generic_runtime_idle() which checks the return value of the
driver's callback and executes pm_runtime_suspend() for the device
unless that value is not 0. If that logic is moved to rpm_idle()
instead, pm_generic_runtime_idle() can be dropped and its users
will not need any .runtime_idle() callbacks any more.
Moreover, the PCI, SCSI, and SATA subsystems' .runtime_idle()
routines, pci_pm_runtime_idle(), scsi_runtime_idle(), and
ata_port_runtime_idle(), respectively, as well as a few drivers'
ones may be simplified if rpm_idle() calls rpm_suspend() after 0 has
been returned by the .runtime_idle() callback executed by it.
To reduce overall code bloat, make the changes described above.
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Tested-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Kevin Hilman <khilman@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
2013-06-04 03:49:52 +08:00
|
|
|
NULL
|
2011-02-23 11:33:17 +08:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Primecells are part of the Advanced Microcontroller Bus Architecture,
|
|
|
|
* so we call the bus "amba".
|
2018-04-28 10:51:58 +08:00
|
|
|
* DMA configuration for platform and AMBA bus is same. So here we reuse
|
|
|
|
* platform's DMA config routine.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2011-02-12 22:58:25 +08:00
|
|
|
struct bus_type amba_bustype = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.name = "amba",
|
2017-06-06 20:16:49 +08:00
|
|
|
.dev_groups = amba_dev_groups,
|
2005-04-17 06:20:36 +08:00
|
|
|
.match = amba_match,
|
2005-11-26 12:04:26 +08:00
|
|
|
.uevent = amba_uevent,
|
2021-01-27 00:58:35 +08:00
|
|
|
.probe = amba_probe,
|
|
|
|
.remove = amba_remove,
|
|
|
|
.shutdown = amba_shutdown,
|
2022-04-18 08:49:52 +08:00
|
|
|
.dma_configure = amba_dma_configure,
|
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers
with the device DMA managed by kernel drivers or user-space applications.
Unfortunately, multiple devices may be placed in the same IOMMU group
because they cannot be isolated from each other. The DMA on these devices
must either be entirely under kernel control or userspace control, never
a mixture. Otherwise the driver integrity is not guaranteed because they
could access each other through the peer-to-peer accesses which by-pass
the IOMMU protection.
This checks and sets the default DMA mode during driver binding, and
cleanups during driver unbinding. In the default mode, the device DMA is
managed by the device driver which handles DMA operations through the
kernel DMA APIs (see Documentation/core-api/dma-api.rst).
For cases where the devices are assigned for userspace control through the
userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/
vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this
default setting in the assumption that the drivers know what they are
doing with the device DMA.
Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure
is currently a problem. As things stand, the IOMMU driver ignored the
initial iommu_probe_device() call when the device was added, since at
that point it had no fwspec yet. In this situation,
{of,acpi}_iommu_configure() are retriggering iommu_probe_device() after
the IOMMU driver has seen the firmware data via .of_xlate to learn that
it actually responsible for the given device. As the result, before
that gets fixed, iommu_use_default_domain() goes at the end, and calls
arch_teardown_dma_ops() if it fails.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Stuart Yoder <stuyoder@gmail.com>
Cc: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20220418005000.897664-5-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
2022-04-18 08:49:53 +08:00
|
|
|
.dma_cleanup = amba_dma_cleanup,
|
2013-12-09 17:38:20 +08:00
|
|
|
.pm = &amba_pm,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
2018-05-15 04:51:55 +08:00
|
|
|
EXPORT_SYMBOL_GPL(amba_bustype);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static int __init amba_init(void)
|
|
|
|
{
|
|
|
|
return bus_register(&amba_bustype);
|
|
|
|
}
|
|
|
|
|
|
|
|
postcore_initcall(amba_init);
|
|
|
|
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
static int amba_proxy_probe(struct amba_device *adev,
|
|
|
|
const struct amba_id *id)
|
|
|
|
{
|
|
|
|
WARN(1, "Stub driver should never match any device.\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct amba_id amba_stub_drv_ids[] = {
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_driver amba_proxy_drv = {
|
|
|
|
.drv = {
|
|
|
|
.name = "amba-proxy",
|
|
|
|
},
|
|
|
|
.probe = amba_proxy_probe,
|
|
|
|
.id_table = amba_stub_drv_ids,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init amba_stub_drv_init(void)
|
|
|
|
{
|
|
|
|
if (!IS_ENABLED(CONFIG_MODULES))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The amba_match() function will get called only if there is at least
|
|
|
|
* one amba driver registered. If all amba drivers are modules and are
|
|
|
|
* only loaded based on uevents, then we'll hit a chicken-and-egg
|
|
|
|
* situation where amba_match() is waiting on drivers and drivers are
|
|
|
|
* waiting on amba_match(). So, register a stub driver to make sure
|
|
|
|
* amba_match() is called even if no amba driver has been registered.
|
|
|
|
*/
|
2024-04-02 17:51:08 +08:00
|
|
|
return __amba_driver_register(&amba_proxy_drv, NULL);
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
}
|
|
|
|
late_initcall_sync(amba_stub_drv_init);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
2024-04-02 17:51:08 +08:00
|
|
|
* __amba_driver_register - register an AMBA device driver
|
2005-04-17 06:20:36 +08:00
|
|
|
* @drv: amba device driver structure
|
2024-04-02 17:51:08 +08:00
|
|
|
* @owner: owning module/driver
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Register an AMBA device driver with the Linux device model
|
|
|
|
* core. If devices pre-exist, the drivers probe function will
|
|
|
|
* be called.
|
|
|
|
*/
|
2024-04-02 17:51:08 +08:00
|
|
|
int __amba_driver_register(struct amba_driver *drv,
|
|
|
|
struct module *owner)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-01-27 00:58:31 +08:00
|
|
|
if (!drv->probe)
|
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2024-04-02 17:51:08 +08:00
|
|
|
drv->drv.owner = owner;
|
2021-01-27 00:58:31 +08:00
|
|
|
drv->drv.bus = &amba_bustype;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return driver_register(&drv->drv);
|
|
|
|
}
|
2024-04-02 17:51:08 +08:00
|
|
|
EXPORT_SYMBOL(__amba_driver_register);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_driver_unregister - remove an AMBA device driver
|
|
|
|
* @drv: AMBA device driver structure to remove
|
|
|
|
*
|
|
|
|
* Unregister an AMBA device driver from the Linux device
|
|
|
|
* model. The device model will call the drivers remove function
|
|
|
|
* for each device the device driver is currently handling.
|
|
|
|
*/
|
|
|
|
void amba_driver_unregister(struct amba_driver *drv)
|
|
|
|
{
|
|
|
|
driver_unregister(&drv->drv);
|
|
|
|
}
|
2021-12-18 16:30:42 +08:00
|
|
|
EXPORT_SYMBOL(amba_driver_unregister);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void amba_device_release(struct device *dev)
|
|
|
|
{
|
|
|
|
struct amba_device *d = to_amba_device(dev);
|
|
|
|
|
2023-10-06 22:57:31 +08:00
|
|
|
fwnode_handle_put(dev_fwnode(&d->dev));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (d->res.parent)
|
|
|
|
release_resource(&d->res);
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
mutex_destroy(&d->periphid_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(d);
|
|
|
|
}
|
|
|
|
|
2016-04-21 14:58:35 +08:00
|
|
|
/**
|
|
|
|
* amba_device_add - add a previously allocated AMBA device structure
|
|
|
|
* @dev: AMBA device allocated by amba_device_alloc
|
|
|
|
* @parent: resource parent for this devices resources
|
|
|
|
*
|
|
|
|
* Claim the resource, and read the device cell ID if not already
|
|
|
|
* initialized. Register the AMBA device with the Linux device
|
|
|
|
* manager.
|
|
|
|
*/
|
|
|
|
int amba_device_add(struct amba_device *dev, struct resource *parent)
|
|
|
|
{
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
int ret;
|
2016-04-21 14:58:35 +08:00
|
|
|
|
2023-10-06 22:57:31 +08:00
|
|
|
fwnode_handle_get(dev_fwnode(&dev->dev));
|
|
|
|
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
ret = request_resource(parent, &dev->res);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-04-21 14:58:35 +08:00
|
|
|
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
/* If primecell ID isn't hard-coded, figure it out */
|
|
|
|
if (!dev->periphid) {
|
|
|
|
/*
|
|
|
|
* AMBA device uevents require reading its pid and cid
|
|
|
|
* registers. To do this, the device must be on, clocked and
|
|
|
|
* out of reset. However in some cases those resources might
|
|
|
|
* not yet be available. If that's the case, we suppress the
|
|
|
|
* generation of uevents until we can read the pid and cid
|
|
|
|
* registers. See also amba_match().
|
|
|
|
*/
|
|
|
|
if (amba_read_periphid(dev))
|
|
|
|
dev_set_uevent_suppress(&dev->dev, true);
|
|
|
|
}
|
2016-04-21 14:58:35 +08:00
|
|
|
|
ARM: 9220/1: amba: Remove deferred device addition
The uevents generated for an amba device need PID and CID information
that's available only when the amba device is powered on, clocked and
out of reset. So, if those resources aren't available, the information
can't be read to generate the uevents. To workaround this requirement,
if the resources weren't available, the device addition was deferred and
retried periodically.
However, this deferred addition retry isn't based on resources becoming
available. Instead, it's retried every 5 seconds and causes arbitrary
probe delays for amba devices and their consumers.
Also, maintaining a separate deferred-probe like mechanism is
maintenance headache.
With this commit, instead of deferring the device addition, we simply
defer the generation of uevents for the device and probing of the device
(because drivers needs PID and CID to match) until the PID and CID
information can be read. This allows us to delete all the amba specific
deferring code and also avoid the arbitrary probing delays.
Cc: Rob Herring <robh@kernel.org>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: patches@armlinux.org.uk
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-07-28 02:19:35 +08:00
|
|
|
ret = device_add(&dev->dev);
|
|
|
|
if (ret)
|
|
|
|
release_resource(&dev->res);
|
2016-04-21 14:58:35 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2011-12-18 19:07:47 +08:00
|
|
|
EXPORT_SYMBOL_GPL(amba_device_add);
|
|
|
|
|
|
|
|
static void amba_device_initialize(struct amba_device *dev, const char *name)
|
|
|
|
{
|
|
|
|
device_initialize(&dev->dev);
|
|
|
|
if (name)
|
|
|
|
dev_set_name(&dev->dev, "%s", name);
|
|
|
|
dev->dev.release = amba_device_release;
|
|
|
|
dev->dev.bus = &amba_bustype;
|
2013-06-27 17:25:33 +08:00
|
|
|
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
|
2020-04-22 18:10:13 +08:00
|
|
|
dev->dev.dma_parms = &dev->dma_parms;
|
2011-12-18 19:07:47 +08:00
|
|
|
dev->res.name = dev_name(&dev->dev);
|
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device
addition"), it became possible for amba_read_periphid() to be invoked
concurrently from two threads for a particular AMBA device.
Consider the case where a thread (T0) is registering an AMBA driver, and
searching for all of the devices it can match with on the AMBA bus.
Suppose that another thread (T1) is executing the deferred probe work,
and is searching through all of the AMBA drivers on the bus for a driver
that matches a particular AMBA device. Assume that both threads begin
operating on the same AMBA device and the device's peripheral ID is
still unknown.
In this scenario, the amba_match() function will be invoked for the
same AMBA device by both threads, which means amba_read_periphid()
can also be invoked by both threads, and both threads will be able
to manipulate the AMBA device's pclk pointer without any synchronization.
It's possible that one thread will initialize the pclk pointer, then the
other thread will re-initialize it, overwriting the previous value, and
both will race to free the same pclk, resulting in a use-after-free for
whichever thread frees the pclk last.
Add a lock per AMBA device to synchronize the handling with detecting the
peripheral ID to avoid the use-after-free scenario.
The following KFENCE bug report helped detect this problem:
==================================================================
BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34
Use-after-free read at 0x(ptrval) (in kfence-#19):
clk_disable+0x14/0x34
amba_read_periphid+0xdc/0x134
amba_match+0x3c/0x84
__driver_attach+0x20/0x158
bus_for_each_dev+0x74/0xc0
bus_add_driver+0x154/0x1e8
driver_register+0x88/0x11c
do_one_initcall+0x8c/0x2fc
kernel_init_freeable+0x190/0x220
kernel_init+0x10/0x108
ret_from_fork+0x14/0x3c
0x0
kfence-#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64
allocated by task 8 on cpu 0 at 11.629931s:
clk_hw_create_clk+0x38/0x134
amba_get_enable_pclk+0x10/0x68
amba_read_periphid+0x28/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
freed by task 8 on cpu 0 at 11.630095s:
amba_read_periphid+0xec/0x134
amba_match+0x3c/0x84
__device_attach_driver+0x2c/0xc4
bus_for_each_drv+0x80/0xd0
__device_attach+0xb0/0x1f0
bus_probe_device+0x88/0x90
deferred_probe_work_func+0x8c/0xc0
process_one_work+0x23c/0x690
worker_thread+0x34/0x488
kthread+0xd4/0xfc
ret_from_fork+0x14/0x3c
0x0
Cc: Saravana Kannan <saravanak@google.com>
Cc: patches@armlinux.org.uk
Fixes: f2d3b9a46e0e ("ARM: 9220/1: amba: Remove deferred device addition")
Reported-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-08-19 01:28:51 +08:00
|
|
|
mutex_init(&dev->periphid_lock);
|
2011-12-18 19:07:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_device_alloc - allocate an AMBA device
|
|
|
|
* @name: sysfs name of the AMBA device
|
|
|
|
* @base: base of AMBA device
|
|
|
|
* @size: size of AMBA device
|
|
|
|
*
|
|
|
|
* Allocate and initialize an AMBA device structure. Returns %NULL
|
|
|
|
* on failure.
|
|
|
|
*/
|
|
|
|
struct amba_device *amba_device_alloc(const char *name, resource_size_t base,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
struct amba_device *dev;
|
|
|
|
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
|
|
if (dev) {
|
|
|
|
amba_device_initialize(dev, name);
|
|
|
|
dev->res.start = base;
|
|
|
|
dev->res.end = base + size - 1;
|
|
|
|
dev->res.flags = IORESOURCE_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(amba_device_alloc);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_device_register - register an AMBA device
|
|
|
|
* @dev: AMBA device to register
|
|
|
|
* @parent: parent memory resource
|
|
|
|
*
|
|
|
|
* Setup the AMBA device, reading the cell ID if present.
|
|
|
|
* Claim the resource, and register the AMBA device with
|
|
|
|
* the Linux device manager.
|
|
|
|
*/
|
|
|
|
int amba_device_register(struct amba_device *dev, struct resource *parent)
|
|
|
|
{
|
|
|
|
amba_device_initialize(dev, dev->dev.init_name);
|
|
|
|
dev->dev.init_name = NULL;
|
|
|
|
|
|
|
|
return amba_device_add(dev, parent);
|
|
|
|
}
|
2021-12-18 16:30:42 +08:00
|
|
|
EXPORT_SYMBOL(amba_device_register);
|
2011-12-18 19:07:47 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_device_put - put an AMBA device
|
|
|
|
* @dev: AMBA device to put
|
|
|
|
*/
|
|
|
|
void amba_device_put(struct amba_device *dev)
|
|
|
|
{
|
|
|
|
put_device(&dev->dev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(amba_device_put);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_device_unregister - unregister an AMBA device
|
|
|
|
* @dev: AMBA device to remove
|
|
|
|
*
|
|
|
|
* Remove the specified AMBA device from the Linux device
|
|
|
|
* manager. All files associated with this object will be
|
|
|
|
* destroyed, and device drivers notified that the device has
|
|
|
|
* been removed. The AMBA device's resources including
|
|
|
|
* the amba_device structure will be freed once all
|
|
|
|
* references to it have been dropped.
|
|
|
|
*/
|
|
|
|
void amba_device_unregister(struct amba_device *dev)
|
|
|
|
{
|
|
|
|
device_unregister(&dev->dev);
|
|
|
|
}
|
2021-12-18 16:30:42 +08:00
|
|
|
EXPORT_SYMBOL(amba_device_unregister);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* amba_request_regions - request all mem regions associated with device
|
|
|
|
* @dev: amba_device structure for device
|
|
|
|
* @name: name, or NULL to use driver name
|
|
|
|
*/
|
|
|
|
int amba_request_regions(struct amba_device *dev, const char *name)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2009-07-29 06:34:59 +08:00
|
|
|
u32 size;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!name)
|
|
|
|
name = dev->dev.driver->name;
|
|
|
|
|
2009-07-29 06:34:59 +08:00
|
|
|
size = resource_size(&dev->res);
|
|
|
|
|
|
|
|
if (!request_mem_region(dev->res.start, size, name))
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -EBUSY;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2021-12-18 16:30:42 +08:00
|
|
|
EXPORT_SYMBOL(amba_request_regions);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
2011-03-31 09:57:33 +08:00
|
|
|
* amba_release_regions - release mem regions associated with device
|
2005-04-17 06:20:36 +08:00
|
|
|
* @dev: amba_device structure for device
|
|
|
|
*
|
|
|
|
* Release regions claimed by a successful call to amba_request_regions.
|
|
|
|
*/
|
|
|
|
void amba_release_regions(struct amba_device *dev)
|
|
|
|
{
|
2009-07-29 06:34:59 +08:00
|
|
|
u32 size;
|
|
|
|
|
|
|
|
size = resource_size(&dev->res);
|
|
|
|
release_mem_region(dev->res.start, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(amba_release_regions);
|