From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:04:54
BCM2711, Raspberry Pi 4's arm64 system on chip, contains a PCIe bus that can't
handle 64-bit accesses to its MMIO address space. The issue has already been
discussed here[1], and it turns out BCM2711 isn't the only broken device in the
wild.
In most cases, the solution to this issue is to convert writeq/readq() to into
their lo_hi/hi_lo variants and the eventual introduction of some amount of
locking. But that's not good enough for every device. For example, on some
arm's SMMU configurations atomic 64-bit accesses are mandatory. This series
tries to introduce a mechanism for drivers to be able to ascertain whether or
not they are allowed to perform 64-bit accesses.
The big question is the amount of granularity needed to deal with this
(think here of distro images):
- Build-time: if a broken platform included in the image, disallow any 64-bit
access. Drivers that need 64-bit accesses could simply bypass the check and
hope for the best. Imposes a performance penalty on otherwise well behaving
platforms, and features that depend on 64bit access might be disabled
unnecessarily. It's simple to implement, yet not very generic/future proof.
- Run-time: allow/disallow 64-bit accesses based on boot time checks (i.e.
check which platform the kernel is running on). Gets rid of all the negative
aspects imposed to well-behaving platforms. Well-behaving buses can't coexist
with broken ones while using all features.
- Per-device: each device has its MMIO access properties and can take decisions
based on its local bus. That said, I'm not aware of a system that absolutely
needs this ATM.
This series implements the third option mainly as a proof of concept.
It's my personal preference on how to deal with this. That said, my main
aim ATM is to settle on a general approach.
Regards,
Nicolas
[1] https://lore.kernel.org/linux-arm-kernel/c188698ca0de3ed6c56a0cf7880e1578aa753077.camel@suse.de/
---
Nicolas Saenz Julienne (13):
dt-bindings: Introduce 64bit-mmio-broken
driver core: Introduce MMIO configuration
of: device: Introduce of_mmio_configure()
driver core: plafrom: Introduce platform_mmio_configure()
pci: Introduce pci_mmio_configure()
device core: Introduce dev_64bit_mmio_supported()
arm64: Mark ARCH_MVEBU as needing broken 64bit MMIO support
arm64: dts: marvell: armada-ap80x: Mark config-space bus as
64bit-mmio-broken
iommu/arm-smmu: Make use of dev_64bit_mmio_supported()
iommu/arm-smmu-impl: Get rid of Marvell's implementation details
arm64: Mark ARCH_BCM2835 as needing broken 64bit MMIO support
ARM: dts: bcm2711: Mark PCIe bus as 64bit-mmio-broken
scsi: megaraid: Make use of dev_64bit_mmio_supported()
.../devicetree/bindings/common-properties.txt | 15 +++++++++++
arch/Kconfig | 8 ++++++
arch/arm/boot/dts/bcm2711.dtsi | 1 +
arch/arm64/Kconfig.platforms | 2 ++
arch/arm64/boot/dts/marvell/armada-ap80x.dtsi | 1 +
drivers/base/dd.c | 6 +++++
drivers/base/platform.c | 9 +++++++
drivers/iommu/arm/arm-smmu/arm-smmu-impl.c | 21 ---------------
drivers/iommu/arm/arm-smmu/arm-smmu.c | 9 +++++++
drivers/iommu/arm/arm-smmu/arm-smmu.h | 9 +++++--
drivers/of/device.c | 19 ++++++++++++++
drivers/pci/pci-driver.c | 26 +++++++++++++++++++
drivers/scsi/megaraid/megaraid_sas_fusion.c | 23 ++++++++--------
include/linux/device.h | 20 ++++++++++++++
include/linux/device/bus.h | 3 +++
include/linux/of_device.h | 8 ++++++
16 files changed, 145 insertions(+), 35 deletions(-)
--
2.30.1
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:04:53
Some devices might inadvertently sit on buses that don't support 64bit
MMIO access, and need a mechanism to query these limitations without
prejudice to other buses in the system (i.e. defaulting to 32bit access
system wide isn't an option).
Introduce a new bus callback, 'mmio_configure(),' which will take care
of populating the relevant device properties based on the bus'
limitations.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
arch/Kconfig | 8 ++++++++
drivers/base/dd.c | 6 ++++++
include/linux/device.h | 3 +++
include/linux/device/bus.h | 3 +++
4 files changed, 20 insertions(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:04:54
Some buses might not be able to handle 64-bit sized MMIO accesses, on
otherwise 64-bit systems. Introduce a boolean property to cater for this
limitation.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
.../devicetree/bindings/common-properties.txt | 15 +++++++++++++++
1 file changed, 15 insertions(+)
@@ -83,3 +83,18 @@ gpio@0 { #gpio-cells = <2>; #daisy-chained-devices = <3>; };++Broken 64-bit buses+-------------------++Some buses might not be able to handle 64-bit sized MMIO accesses, on otherwise+64-bit systems. This property is only relevant to MMIO bus nodes.++Optional properties:+ - 64bit-mmio-broken: Boolean++Example:+pcie@0 {+ compatible = "name";+ 64bit-mmio-broken;+};
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:05:32
The function will traverse the platform device's bus hierarchy and set
the relevant MMIO access flags.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/base/platform.c | 9 +++++++++
1 file changed, 9 insertions(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:05:55
The function will traverse a device's bus hierarchy looking for MMIO
limited buses. If found it'll populate the relevant struct device
quirks.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/of/device.c | 19 +++++++++++++++++++
include/linux/of_device.h | 8 ++++++++
2 files changed, 27 insertions(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:06:07
This helper function will be help drivers ascertain whether they can use
64-bit memory accesses.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
include/linux/device.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:07:20
The function will traverse the pci device's bus hierarchy and set
the relevant MMIO access flags.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/pci/pci-driver.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:07:20
The bus AP806's IOMMU sits on can't handle 64bit MMIO accesses[1]. So
select 'ARCH_HAS_64BIT_MMIO_BROKEN' for the platform.
Signed-off-by: Nicolas Saenz Julienne <redacted>
[1] See Armada-AP806 erratum #582743
---
arch/arm64/Kconfig.platforms | 1 +
1 file changed, 1 insertion(+)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:07:22
Some arm SMMU implementations might sit on a bus that doesn't support
64bit memory accesses. In that case default to using hi_lo_{readq,
writeq}() and BUG if such platform tries to use AArch64 formats as they
rely on writeq()'s atomicity.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/iommu/arm/arm-smmu/arm-smmu.c | 9 +++++++++
drivers/iommu/arm/arm-smmu/arm-smmu.h | 9 +++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:07:58
arm-smmu can now deal with integrations on buses that don't support
64bit MMIO accesses. No need to create a special case for that on
Marvell's integration.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/iommu/arm/arm-smmu/arm-smmu-impl.c | 21 ---------------------
1 file changed, 21 deletions(-)
From: Nicolas Saenz Julienne <hidden> Date: 2021-02-26 14:09:49
Instead of relying on defines use dev_64bit_mmio_supported(), which
provides the same functionality. On top of that convert the
implementation to lo_hi_writeq(), for a cleaner end result.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/scsi/megaraid/megaraid_sas_fusion.c | 23 ++++++++++-----------
1 file changed, 11 insertions(+), 12 deletions(-)
I see your patch changes the code to the lo_hi_writeq() accessor,
and it also fixes the endianness bug (double byteswap on big-endian),
but it does not fix the spinlock bug (writel on pci leaks out of the lock
unless it's followed by a read).
I'd suggest splitting the bugfix from the cleanup here, and fixing both
of the bugs while you're at it.
Arnd
I see your patch changes the code to the lo_hi_writeq() accessor,
and it also fixes the endianness bug (double byteswap on big-endian),
but it does not fix the spinlock bug (writel on pci leaks out of the lock
unless it's followed by a read).
On second look, it seems your patch breaks the byteorder logic,
rather than fixing it. It would seem better to leave it unchanged
then, or to send a separate rework of the endianness conversion if
you think it is wrong.
Arnd
I see this pattern repeat across multiple drivers. I think Christoph
had originally
suggested folding the if/else logic into the writel_relaxed() that is defined in
include/linux/io-64-nonatomic-hi-lo.h, but of course that doesn't work if you
need to pass a device pointer.
It might still make sense to have another wrapper in that same file though,
something like
static inline hi_lo_writeq_relaxed_if_possible(struct device *dev, __u64 val,
volatile void __iomem *addr)
{
if (dev_64bit_mmio_supported(smmu->dev)) {
readq_relaxed(arm_smmu_page(smmu, page) + offset);
} else {
writel_relaxed(val >> 32, addr + 4);
writel_relaxed(val, addr);
}
}
Arnd
From: Robin Murphy <robin.murphy@arm.com> Date: 2021-03-02 11:22:41
On 2021-02-26 14:03, Nicolas Saenz Julienne wrote:
quoted hunk
Some arm SMMU implementations might sit on a bus that doesn't support
64bit memory accesses. In that case default to using hi_lo_{readq,
writeq}() and BUG if such platform tries to use AArch64 formats as they
rely on writeq()'s atomicity.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/iommu/arm/arm-smmu/arm-smmu.c | 9 +++++++++
drivers/iommu/arm/arm-smmu/arm-smmu.h | 9 +++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
@@ -1889,6 +1889,15 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)smmu->features|=ARM_SMMU_FEAT_FMT_AARCH64_64K;}+/*+*64bitaccessesnotpossiblethroughtheinterconnect,AArch64+*formatsdependonit.+*/+BUG_ON(!dev_64bit_mmio_supported(smmu->dev)&&+smmu->features&(ARM_SMMU_FEAT_FMT_AARCH64_4K|+ARM_SMMU_FEAT_FMT_AARCH64_16K|+ARM_SMMU_FEAT_FMT_AARCH64_64K));
No. Crashing the kernel in a probe routine which is free to fail is
unacceptable either way, but guaranteeing failure in the case that the
workaround *would* be required is doubly so.
Basically, this logic is backwards - if you really wanted to handle it
generically, this would be the point at which you'd need to actively
suppress all the detected hardware features which depend on 64-bit
atomicity, not complain about them.
quoted hunk
+
if (smmu->impl && smmu->impl->cfg_probe) {
ret = smmu->impl->cfg_probe(smmu);
if (ret)
As Arnd pointed out, this is in completely the wrong place. Also, in
general it doesn't work if the implementation already needs a hook to
filter or override register accesses for any other reason. TBH I'm not
convinced that this isn't *more* of a mess than handling it on a
SoC-specific basis...
Robin.
From: Robin Murphy <robin.murphy@arm.com> Date: 2021-03-02 12:04:31
On 2021-02-26 14:02, Nicolas Saenz Julienne wrote:
Some devices might inadvertently sit on buses that don't support 64bit
MMIO access, and need a mechanism to query these limitations without
prejudice to other buses in the system (i.e. defaulting to 32bit access
system wide isn't an option).
Introduce a new bus callback, 'mmio_configure(),' which will take care
of populating the relevant device properties based on the bus'
limitations.
Devil's advocate: there already exist workarounds for 8-bit and/or
16-bit accesses not working in various places, does it make sense for a
64-bit workaround to be so wildly different and disjoint?
As mentioned previously, 32-bit systems may not need the overrides for
kernel I/O accessors, but they could still need the same workarounds for
the memory-mapping implications (if this is to be a proper generic
mechanism).
+ default n
Tip: it is always redundant to state that.
Robin.
quoted hunk
+ help
+ Arch might contain busses unable to perform 64bit mmio accessses on
+ an otherwise 64bit system.
+
source "kernel/gcov/Kconfig"
source "scripts/gcc-plugins/Kconfig"
From: Robin Murphy <robin.murphy@arm.com> Date: 2021-03-02 12:15:42
On 2021-02-26 14:03, Nicolas Saenz Julienne wrote:
arm-smmu can now deal with integrations on buses that don't support
64bit MMIO accesses. No need to create a special case for that on
Marvell's integration.
This breaks compatibility with existing DTs.
Robin.
I see this pattern repeat across multiple drivers. I think Christoph
had originally
suggested folding the if/else logic into the writel_relaxed() that is defined in
include/linux/io-64-nonatomic-hi-lo.h, but of course that doesn't work if you
need to pass a device pointer.
It might still make sense to have another wrapper in that same file though,
something like
static inline hi_lo_writeq_relaxed_if_possible(struct device *dev, __u64 val,
volatile void __iomem *addr)
{
if (dev_64bit_mmio_supported(smmu->dev)) {
readq_relaxed(arm_smmu_page(smmu, page) + offset);
} else {
writel_relaxed(val >> 32, addr + 4);
writel_relaxed(val, addr);
}
}
I like the idea. I'll try to integrate it into the next revision.
Regards,
Nicolas
From: Nicolas Saenz Julienne <hidden> Date: 2021-03-02 16:23:51
Hi Robin, thanks for taking the time to look at this.
On Tue, 2021-03-02 at 11:07 +0000, Robin Murphy wrote:
On 2021-02-26 14:03, Nicolas Saenz Julienne wrote:
quoted
Some arm SMMU implementations might sit on a bus that doesn't support
64bit memory accesses. In that case default to using hi_lo_{readq,
writeq}() and BUG if such platform tries to use AArch64 formats as they
rely on writeq()'s atomicity.
Signed-off-by: Nicolas Saenz Julienne <redacted>
---
drivers/iommu/arm/arm-smmu/arm-smmu.c | 9 +++++++++
drivers/iommu/arm/arm-smmu/arm-smmu.h | 9 +++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
@@ -1889,6 +1889,15 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K;
}
+ /*
+ * 64bit accesses not possible through the interconnect, AArch64
+ * formats depend on it.
+ */
+ BUG_ON(!dev_64bit_mmio_supported(smmu->dev) &&
+ smmu->features & (ARM_SMMU_FEAT_FMT_AARCH64_4K |
+ ARM_SMMU_FEAT_FMT_AARCH64_16K |
+ ARM_SMMU_FEAT_FMT_AARCH64_64K));
No. Crashing the kernel in a probe routine which is free to fail is
unacceptable either way, but guaranteeing failure in the case that the
workaround *would* be required is doubly so.
Basically, this logic is backwards - if you really wanted to handle it
generically, this would be the point at which you'd need to actively
suppress all the detected hardware features which depend on 64-bit
atomicity, not complain about them.
Understood.
quoted
+
if (smmu->impl && smmu->impl->cfg_probe) {
ret = smmu->impl->cfg_probe(smmu);
if (ret)
As Arnd pointed out, this is in completely the wrong place. Also, in
Yes, sorry for that, not too proud of it.
general it doesn't work if the implementation already needs a hook to
filter or override register accesses for any other reason. TBH I'm not
I'm not sure I get your point here, 'smmu->impl' has precedence over the MMIO
capability check. Custom implementations would still get their callbacks.
convinced that this isn't *more* of a mess than handling it on a
SoC-specific basis...
I see your point.
Just to explain why I went to these lengths: my understanding is that the
specifics of how to perform 32bit accesses to SMMU's 64bit registers is defined
in spec. So it made sense to move it into the non implementation dependent side
of the driver.
All in all, I'll think of something simpler.
Regards,
Nicolas
From: Nicolas Saenz Julienne <hidden> Date: 2021-03-02 16:30:32
On Tue, 2021-03-02 at 11:40 +0000, Robin Murphy wrote:
On 2021-02-26 14:03, Nicolas Saenz Julienne wrote:
quoted
arm-smmu can now deal with integrations on buses that don't support
64bit MMIO accesses. No need to create a special case for that on
Marvell's integration.
This breaks compatibility with existing DTs.
Yes. On top of that, I had a brief word with robh on the topic of DT
properties. I'm going to explore alternatives that don't depend on it.
Regards,
Nicolas
From: Nicolas Saenz Julienne <hidden> Date: 2021-03-02 16:38:51
Hi Robin,
On Tue, 2021-03-02 at 11:29 +0000, Robin Murphy wrote:
On 2021-02-26 14:02, Nicolas Saenz Julienne wrote:
quoted
Some devices might inadvertently sit on buses that don't support 64bit
MMIO access, and need a mechanism to query these limitations without
prejudice to other buses in the system (i.e. defaulting to 32bit access
system wide isn't an option).
Introduce a new bus callback, 'mmio_configure(),' which will take care
of populating the relevant device properties based on the bus'
limitations.
Devil's advocate: there already exist workarounds for 8-bit and/or
16-bit accesses not working in various places, does it make sense for a
64-bit workaround to be so wildly different and disjoint?
As mentioned previously, 32-bit systems may not need the overrides for
kernel I/O accessors, but they could still need the same workarounds for
the memory-mapping implications (if this is to be a proper generic
mechanism).
As Arnd pointed out, this is in completely the wrong place. Also, in
general it doesn't work if the implementation already needs a hook to
filter or override register accesses for any other reason. TBH I'm not
convinced that this isn't *more* of a mess than handling it on a
SoC-specific basis...
I think the main problem for handling it in a SoC specific way is that there is
no device-independent way to do a 64-bit store as two 32-bit stores:
- some devices need hi_lo_writeq_relaxed(), others need lo_hi_writeq_relaxed(),
and some absolutely require 64-bit stores and cannot work at all behind a
broken PCI bus.
- if the driver requires the store to be atomic, it needs to use a spinlock
around the two writel(), but if the register is on a PCI bus or mapped
with page attributes that allow posted writes (like arm64 ioremap), then
you may need to read back the register before spin_unlock to serialize
them properly. However, reading back an mmio register is slow and can
have side-effects, so you can't put that in driver-independent code either.
Arnd