Skip to content

Commit b413ea6

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 c77e628 commit b413ea6

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
@@ -1732,6 +1732,36 @@ config ARM64_TAGGED_ADDR_ABI
17321732
to system calls as pointer arguments. For details, see
17331733
Documentation/arch/arm64/tagged-address-abi.rst.
17341734

1735+
config ARM64_WORKAROUND_NC_TO_NGNRE
1736+
bool "Workaround: Convert MT_NORMAL_NC to Device-nGnRE"
1737+
default y
1738+
help
1739+
This option enables a workaround that converts the MT_NORMAL_NC
1740+
(Non-Cacheable) memory attribute to Device-nGnRE memory type in
1741+
MAIR_EL1 (Memory Attribute Indirection Register).
1742+
1743+
This workaround is useful for hardware that requires stricter
1744+
memory ordering or has issues with Non-Cacheable memory mappings.
1745+
1746+
A new memory type index MT_NORMAL_NC_DMA (Attr5) has been introduced
1747+
specifically for DMA coherent memory mappings (pgprot_dmacoherent),
1748+
configured with the same Normal Non-Cacheable attribute (0x44) as
1749+
MT_NORMAL_NC (Attr2). When this workaround is enabled, it converts
1750+
the NC attribute to Device-nGnRE (0x04), and pgprot_dmacoherent
1751+
behavior remains the same as before.
1752+
1753+
The workaround uses the ARM64 alternatives framework for efficient
1754+
runtime patching with no performance overhead when disabled.
1755+
1756+
This workaround can only be enabled at boot time via kernel command
1757+
line parameter. Runtime changes are not supported because CPU
1758+
alternatives cannot be re-patched after boot.
1759+
1760+
Boot-time activation (kernel command line):
1761+
mair_el1_nc_to_ngnre=1
1762+
1763+
If unsure, say Y.
1764+
17351765
menuconfig COMPAT
17361766
bool "Kernel support for 32-bit EL0"
17371767
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
@@ -795,9 +795,15 @@ static inline void __set_puds(struct mm_struct *mm,
795795
* requires strict alignment and can also force write responses to come from the
796796
* endpoint.
797797
*/
798+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
799+
#define pgprot_dmacoherent(prot) \
800+
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
801+
PTE_ATTRINDX(MT_NORMAL_NC_DMA) | PTE_PXN | PTE_UXN)
802+
#else
798803
#define pgprot_dmacoherent(prot) \
799804
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
800805
PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
806+
#endif
801807

802808
#define __HAVE_PHYS_MEM_ACCESS_PROT
803809
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;
@@ -565,6 +570,97 @@ static const struct midr_range erratum_ac04_cpu_23_list[] = {
565570
};
566571
#endif
567572

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

arch/arm64/kvm/mmu.c

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

1631-
vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
1631+
if (!cpus_have_cap(ARM64_WORKAROUND_NC_TO_NGNRE))
1632+
vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED;
16321633

16331634
vm_flags = vma->vm_flags;
16341635

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
/**
@@ -467,6 +468,14 @@ SYM_FUNC_START(__cpu_setup)
467468
tcr .req x16
468469
tcr2 .req x15
469470
mov_q mair, MAIR_EL1_SET
471+
472+
#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE
473+
alternative_if ARM64_WORKAROUND_NC_TO_NGNRE
474+
mov x9, #MAIR_ATTR_DEVICE_nGnRE
475+
bfi mair, x9, #(MT_NORMAL_NC * 8), #8
476+
alternative_else_nop_endif
477+
#endif
478+
470479
mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \
471480
TCR_SHARED | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
472481
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
@@ -112,6 +112,7 @@ WORKAROUND_CAVIUM_TX2_219_PRFM
112112
WORKAROUND_CAVIUM_TX2_219_TVM
113113
WORKAROUND_CLEAN_CACHE
114114
WORKAROUND_DEVICE_LOAD_ACQUIRE
115+
WORKAROUND_NC_TO_NGNRE
115116
WORKAROUND_NVIDIA_CARMEL_CNP
116117
WORKAROUND_PMUV3_IMPDEF_TRAPS
117118
WORKAROUND_QCOM_FALKOR_E1003

0 commit comments

Comments
 (0)