Skip to content

Commit 3b9023e

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. Signed-off-by: Shanker Donthineni <sdonthineni@nvidia.com> Signed-off-by: Matthew R. Ochs <mochs@nvidia.com> Acked-by: Nirmoy Das <nirmoyd@nvidia.com> Acked-by: Carol L. Soto <csoto@nvidia.com> Signed-off-by: Matthew R. Ochs <mochs@nvidia.com>
1 parent d602686 commit 3b9023e

File tree

7 files changed

+155
-1
lines changed

7 files changed

+155
-1
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/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)