arm64: errata: work around NVIDIA Olympus device store/load ordering

On systems with NVIDIA Olympus cores, a Device-nGnR* load can be
observed by a peripheral before an older, non-overlapping Device-nGnR*
store to the same peripheral. This breaks the program-order guarantee
that software expects for Device-nGnR* accesses and can leave a
peripheral in an incorrect state.

The erratum can occur only when all of the following apply:

  - A PE executes a Device-nGnR* store followed by a younger
    Device-nGnR* load.
  - The store is not a store-release.
  - The accesses target the same peripheral and do not overlap in bytes.
  - There is at most one intervening Device-nGnR* store in program
    order, and there are no intervening Device-nGnR* loads.
  - There is no DSB or full DMB between the store and the load.
  - Specific microarchitectural and timing conditions occur.

Insert a DMB OSH immediately before each raw MMIO load on affected CPUs.
As a full barrier, DMB OSH orders the older Device store before the
younger Device load and prevents the erroneous observation.

Add the barrier directly to the __raw_read*() helpers, independently of
the existing device-load-acquire alternative. On affected CPUs this adds
one DMB OSH per raw MMIO load, including each load used by
memcpy_fromio(). On unaffected CPUs the alternative remains a NOP.

Co-developed-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Vikram Sethi <vsethi@nvidia.com>
Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com>
Link: https://lore.kernel.org/all/akPQ8F3OgER621UP@willie-the-truck/
Reviewed-by: Vladimir Murzin <vladimir.murzin@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
Shanker Donthineni 2026-07-15 15:48:56 -05:00 committed by Will Deacon
parent 62e11a7fde
commit 12aab25ca5
5 changed files with 45 additions and 4 deletions

View File

@ -304,6 +304,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Olympus core | T410-OLY-1027 | NVIDIA_OLYMPUS_1027_ERRATUM |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | Olympus core | T410-OLY-1029 | ARM64_ERRATUM_4118414 |
+----------------+-----------------+-----------------+-----------------------------+
| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |

View File

@ -1404,6 +1404,28 @@ config NVIDIA_CARMEL_CNP_ERRATUM
If unsure, say Y.
config NVIDIA_OLYMPUS_1027_ERRATUM
bool "NVIDIA Olympus: device store/load ordering erratum"
default y
help
This option adds an alternative code sequence to work around an
NVIDIA Olympus core erratum where a Device-nGnR* store can be
observed by a peripheral after a younger Device-nGnR* load to the
same peripheral. This breaks the program order that drivers rely
on for MMIO and can leave a device in an incorrect state.
The workaround inserts a DMB OSH immediately before raw MMIO loads.
The erratum cannot occur when a DMB that orders loads appears
between the store and load, preventing the younger load from being
observed before the older store.
The alternatives framework patches in DMB OSH only when an affected
CPU is detected. Other CPUs execute a NOP in its place. Disabling
this option leaves the original MMIO read instruction stream
unchanged.
If unsure, say Y.
config ROCKCHIP_ERRATUM_3568002
bool "Rockchip 3568002: GIC600 can not access physical addresses higher than 4GB"
default y

View File

@ -54,7 +54,9 @@ static __always_inline void __raw_writeq(u64 val, volatile void __iomem *addr)
static __always_inline u8 __raw_readb(const volatile void __iomem *addr)
{
u8 val;
asm volatile(ALTERNATIVE("ldrb %w0, [%1]",
asm volatile(ALTERNATIVE("nop", "dmb osh",
ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
ALTERNATIVE("ldrb %w0, [%1]",
"ldarb %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
@ -66,7 +68,9 @@ static __always_inline u16 __raw_readw(const volatile void __iomem *addr)
{
u16 val;
asm volatile(ALTERNATIVE("ldrh %w0, [%1]",
asm volatile(ALTERNATIVE("nop", "dmb osh",
ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
ALTERNATIVE("ldrh %w0, [%1]",
"ldarh %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
@ -77,7 +81,9 @@ static __always_inline u16 __raw_readw(const volatile void __iomem *addr)
static __always_inline u32 __raw_readl(const volatile void __iomem *addr)
{
u32 val;
asm volatile(ALTERNATIVE("ldr %w0, [%1]",
asm volatile(ALTERNATIVE("nop", "dmb osh",
ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
ALTERNATIVE("ldr %w0, [%1]",
"ldar %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
@ -88,7 +94,9 @@ static __always_inline u32 __raw_readl(const volatile void __iomem *addr)
static __always_inline u64 __raw_readq(const volatile void __iomem *addr)
{
u64 val;
asm volatile(ALTERNATIVE("ldr %0, [%1]",
asm volatile(ALTERNATIVE("nop", "dmb osh",
ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
ALTERNATIVE("ldr %0, [%1]",
"ldar %0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));

View File

@ -850,6 +850,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_RANGE_LIST(cnp_erratum_cpus),
},
#endif
#ifdef CONFIG_NVIDIA_OLYMPUS_1027_ERRATUM
{
/* NVIDIA Olympus core */
.desc = "NVIDIA Olympus device store/load ordering erratum",
.capability = ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027,
ERRATA_MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
},
#endif
#ifdef CONFIG_ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
{
/*

View File

@ -121,6 +121,7 @@ WORKAROUND_CAVIUM_TX2_219_TVM
WORKAROUND_CLEAN_CACHE
WORKAROUND_DEVICE_LOAD_ACQUIRE
WORKAROUND_DISABLE_CNP
WORKAROUND_NVIDIA_OLYMPUS_1027
WORKAROUND_PMUV3_IMPDEF_TRAPS
WORKAROUND_QCOM_FALKOR_E1003
WORKAROUND_QCOM_ORYON_CNTVOFF