Skip to content

Commit e1bd520

Browse files
shankerd04nvmochs
authored andcommitted
NVIDIA: VR: SAUCE: arm64: Add workaround to convert MT_NORMAL_NC to Device-nGnRE
Add CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE configuration option that enables conversion of MT_NORMAL_NC (Normal Non-Cacheable) memory attribute to Device-nGnRE memory type in MAIR_EL1 for hardware that requires stricter memory ordering or has issues with Non-Cacheable memory mappings. Key changes: 1. New memory type MT_NORMAL_NC_DMA (Attr5): - Introduced specifically for DMA coherent memory mappings - Configured with the same Normal Non-Cacheable attribute (0x44) as MT_NORMAL_NC (Attr2) by default - pgprot_dmacoherent uses MT_NORMAL_NC_DMA when workaround is enabled, MT_NORMAL_NC otherwise 2. MAIR_EL1 conversion via alternatives framework: - arch/arm64/mm/proc.S uses ARM64 alternatives to patch MAIR_EL1 during early boot - Converts MT_NORMAL_NC (Attr2) from 0x44 to 0x04 (Device-nGnRE) using efficient bfi instruction - MT_NORMAL_NC_DMA (Attr5) keeps the same attribute value as MT_NORMAL_NC originally had - Zero performance overhead when workaround is disabled 3. Boot-time configuration: - Enabled via kernel command line: mair_el1_nc_to_ngnre=1 - Boot CPU fixup in enable_nc_to_ngnre() applies conversion before alternatives are patched - Secondary CPUs automatically use patched alternatives in __cpu_setup - Runtime changes not supported as alternatives cannot be re-patched after boot 4. Errata framework integration: - Registered in arm64_errata[] array as ARM64_WORKAROUND_NC_TO_NGNRE - Capability type: ARM64_CPUCAP_BOOT_CPU_FEATURE - Uses cpucap_is_possible() for build-time capability checking The workaround preserves pgprot_dmacoherent behavior while allowing MT_NORMAL_NC to be converted to Device memory type for other mappings that may be affected by hardware issues. Ensure NC memory attribute assignment is prevented for passthrough device MMIO regions. Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com> Signed-off-by: Matthew R. Ochs <mochs@nvidia.com>
1 parent d602686 commit e1bd520

File tree

8 files changed

+157
-2
lines changed

8 files changed

+157
-2
lines changed

arch/arm64/Kconfig

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,36 @@ config ARM64_TAGGED_ADDR_ABI
16951695
to system calls as pointer arguments. For details, see
16961696
Documentation/arch/arm64/tagged-address-abi.rst.
16971697

1698+
config ARM64_WORKAROUND_NC_TO_NGNRE
1699+
bool "Workaround: Convert MT_NORMAL_NC to Device-nGnRE"
1700+
default y
1701+
help
1702+
This option enables a workaround that converts the MT_NORMAL_NC
1703+
(Non-Cacheable) memory attribute to Device-nGnRE memory type in
1704+
MAIR_EL1 (Memory Attribute Indirection Register).
1705+
1706+
This workaround is useful for hardware that requires stricter
1707+
memory ordering or has issues with Non-Cacheable memory mappings.
1708+
1709+
A new memory type index MT_NORMAL_NC_DMA (Attr5) has been introduced
1710+
specifically for DMA coherent memory mappings (pgprot_dmacoherent),
1711+
configured with the same Normal Non-Cacheable attribute (0x44) as
1712+
MT_NORMAL_NC (Attr2). When this workaround is enabled, it converts
1713+
the NC attribute to Device-nGnRE (0x04), and pgprot_dmacoherent
1714+
behavior remains the same as before.
1715+
1716+
The workaround uses the ARM64 alternatives framework for efficient
1717+
runtime patching with no performance overhead when disabled.
1718+
1719+
This workaround can only be enabled at boot time via kernel command
1720+
line parameter. Runtime changes are not supported because CPU
1721+
alternatives cannot be re-patched after boot.
1722+
1723+
Boot-time activation (kernel command line):
1724+
mair_el1_nc_to_ngnre=1
1725+
1726+
If unsure, say Y.
1727+
16981728
menuconfig COMPAT
16991729
bool "Kernel support for 32-bit EL0"
17001730
depends on ARM64_4K_PAGES || EXPERT

arch/arm64/include/asm/cpucaps.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ cpucap_is_possible(const unsigned int cap)
7373
return true;
7474
case ARM64_HAS_PMUV3:
7575
return IS_ENABLED(CONFIG_HW_PERF_EVENTS);
76+
case ARM64_WORKAROUND_NC_TO_NGNRE:
77+
return IS_ENABLED(CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE);
7678
}
7779

7880
return true;

arch/arm64/include/asm/memory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
#define MT_NORMAL_NC 2
174174
#define MT_DEVICE_nGnRnE 3
175175
#define MT_DEVICE_nGnRE 4
176+
#define MT_NORMAL_NC_DMA 5
176177

177178
/*
178179
* Memory types for Stage-2 translation

arch/arm64/include/asm/pgtable.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,9 +800,15 @@ static inline void __set_puds(struct mm_struct *mm,
800800
* requires strict alignment and can also force write responses to come from the
801801
* endpoint.
802802
*/
803+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
804+
#define pgprot_dmacoherent(prot) \
805+
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
806+
PTE_ATTRINDX(MT_NORMAL_NC_DMA) | PTE_PXN | PTE_UXN)
807+
#else
803808
#define pgprot_dmacoherent(prot) \
804809
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
805810
PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
811+
#endif
806812

807813
#define __HAVE_PHYS_MEM_ACCESS_PROT
808814
struct file;

arch/arm64/kernel/cpu_errata.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@
88
#include <linux/arm-smccc.h>
99
#include <linux/types.h>
1010
#include <linux/cpu.h>
11+
#include <linux/arm-smccc.h>
1112
#include <asm/cpu.h>
1213
#include <asm/cputype.h>
1314
#include <asm/cpufeature.h>
1415
#include <asm/kvm_asm.h>
1516
#include <asm/smp_plat.h>
17+
#include <asm/memory.h>
18+
#include <asm/barrier.h>
19+
#include <asm/tlbflush.h>
20+
#include <asm/sysreg.h>
1621

1722
static u64 target_impl_cpu_num;
1823
static struct target_impl_cpu *target_impl_cpus;
@@ -566,6 +571,97 @@ static const struct midr_range erratum_ac04_cpu_23_list[] = {
566571
};
567572
#endif
568573

574+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
575+
/*
576+
* MAIR_EL1 MT_NORMAL_NC to Device-nGnRE Conversion
577+
*
578+
* Boot-time workaround that converts MT_NORMAL_NC attribute to Device-nGnRE
579+
* (0x04) via kernel parameter for hardware-specific issues.
580+
*
581+
*/
582+
583+
/*
584+
* Flag indicating if MT_NORMAL_NC to nGnRE conversion is enabled
585+
*/
586+
static int mair_el1_nc_to_ngnre __read_mostly = -1;
587+
588+
/*
589+
* Parse kernel command line parameter at boot:
590+
* mair_el1_nc_to_ngnre=1
591+
* Enables MT_NORMAL_NC to Device-nGnRE conversion
592+
*/
593+
static int __init mair_el1_nc_setup(char *str)
594+
{
595+
bool enable;
596+
int ret;
597+
598+
ret = kstrtobool(str, &enable);
599+
if (ret)
600+
return ret;
601+
602+
mair_el1_nc_to_ngnre = enable ? 1 : 0;
603+
604+
pr_info("MAIR_EL1: MT_NORMAL_NC to Device-nGnRE conversion %s\n",
605+
enable ? "enabled" : "disabled");
606+
607+
return 0;
608+
}
609+
early_param("mair_el1_nc_to_ngnre", mair_el1_nc_setup);
610+
611+
/* Cpufeature capability check for MAIR NC to nGnRE workaround */
612+
static bool has_nc_ngnre_workaround(const struct arm64_cpu_capabilities *entry,
613+
int scope)
614+
{
615+
/* Lazy initialization: check only once */
616+
if (mair_el1_nc_to_ngnre == -1) {
617+
if ((arm_smccc_get_soc_id_version() == 0x036b0410) &&
618+
(arm_smccc_get_soc_id_revision() < 5)) {
619+
mair_el1_nc_to_ngnre = 1;
620+
return true;
621+
}
622+
mair_el1_nc_to_ngnre = 0;
623+
}
624+
625+
return mair_el1_nc_to_ngnre > 0;
626+
}
627+
628+
/*
629+
* Called by cpufeature framework when CPU comes online
630+
* For boot CPU: alternatives not yet patched, so apply NC to nGnRE here
631+
* For secondary CPUs: alternatives already patched in __cpu_setup
632+
*/
633+
static void enable_nc_to_ngnre(struct arm64_cpu_capabilities const *cap)
634+
{
635+
u64 attr_mask, current_mair, new_mair;
636+
u8 current_attr;
637+
638+
if (mair_el1_nc_to_ngnre <= 0)
639+
return;
640+
641+
current_mair = read_sysreg(mair_el1);
642+
643+
/* Check if MT_NORMAL_NC is already Device-nGnRE */
644+
attr_mask = GENMASK_ULL((MT_NORMAL_NC * 8) + 7, MT_NORMAL_NC * 8);
645+
current_attr = FIELD_GET(attr_mask, current_mair);
646+
647+
/* Already set via alternatives (secondary CPU case) */
648+
if (current_attr == MAIR_ATTR_DEVICE_nGnRE)
649+
return;
650+
651+
/* Apply override for boot CPU */
652+
new_mair = (current_mair & ~attr_mask) |
653+
((u64)MAIR_ATTR_DEVICE_nGnRE << (MT_NORMAL_NC * 8));
654+
655+
write_sysreg(new_mair, mair_el1);
656+
isb();
657+
local_flush_tlb_all();
658+
659+
pr_info("CPU%d: MAIR_EL1 updated 0x%016llx -> 0x%016llx\n",
660+
smp_processor_id(), current_mair, new_mair);
661+
}
662+
663+
#endif /* CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE */
664+
569665
const struct arm64_cpu_capabilities arm64_errata[] = {
570666
#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
571667
{
@@ -907,6 +1003,15 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
9071003
.matches = has_impdef_pmuv3,
9081004
.cpu_enable = cpu_enable_impdef_pmuv3_traps,
9091005
},
1006+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
1007+
{
1008+
.desc = "MAIR_EL1 NC to nGnRE",
1009+
.capability = ARM64_WORKAROUND_NC_TO_NGNRE,
1010+
.type = ARM64_CPUCAP_BOOT_CPU_FEATURE,
1011+
.matches = has_nc_ngnre_workaround,
1012+
.cpu_enable = enable_nc_to_ngnre,
1013+
},
1014+
#endif
9101015
{
9111016
}
9121017
};

arch/arm64/kvm/mmu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1724,7 +1724,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
17241724
gfn = ipa >> PAGE_SHIFT;
17251725
mte_allowed = kvm_vma_mte_allowed(vma);
17261726

1727-
vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
1727+
if (!cpus_have_cap(ARM64_WORKAROUND_NC_TO_NGNRE))
1728+
vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
17281729

17291730
vm_flags = vma->vm_flags;
17301731

arch/arm64/mm/proc.S

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@
6666
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, MT_DEVICE_nGnRE) | \
6767
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC) | \
6868
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) | \
69-
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED))
69+
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED) | \
70+
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC_DMA))
7071

7172
#ifdef CONFIG_CPU_PM
7273
/**
@@ -480,6 +481,14 @@ SYM_FUNC_START(__cpu_setup)
480481
tcr .req x16
481482
tcr2 .req x15
482483
mov_q mair, MAIR_EL1_SET
484+
485+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
486+
alternative_if ARM64_WORKAROUND_NC_TO_NGNRE
487+
mov x9, #MAIR_ATTR_DEVICE_nGnRE
488+
bfi mair, x9, #(MT_NORMAL_NC * 8), #8
489+
alternative_else_nop_endif
490+
#endif
491+
483492
mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \
484493
TCR_SHARED | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
485494
TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS

arch/arm64/tools/cpucaps

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ WORKAROUND_CAVIUM_TX2_219_PRFM
113113
WORKAROUND_CAVIUM_TX2_219_TVM
114114
WORKAROUND_CLEAN_CACHE
115115
WORKAROUND_DEVICE_LOAD_ACQUIRE
116+
WORKAROUND_NC_TO_NGNRE
116117
WORKAROUND_NVIDIA_CARMEL_CNP
117118
WORKAROUND_PMUV3_IMPDEF_TRAPS
118119
WORKAROUND_QCOM_FALKOR_E1003

0 commit comments

Comments
 (0)