From 48462620960069b043cbbf809c48a2bfcffd9368 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 27 Nov 2025 00:31:30 +0200 Subject: [PATCH 01/43] arm64: simplify arch_uprobe_xol_was_trapped return convert arch_uprobe_xol_was_trapped() from explicit if/return true return false pattern to direct boolean expression return in arch/arm64/kernel/probes/uprobes.c Signed-off-by: Osama Abdelkader Reviewed-by: Anshuman Khandual Acked-by: Masami Hiramatsu (Google) Signed-off-by: Will Deacon --- arch/arm64/kernel/probes/uprobes.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c index 941668800aea..c451ca17a656 100644 --- a/arch/arm64/kernel/probes/uprobes.c +++ b/arch/arm64/kernel/probes/uprobes.c @@ -103,10 +103,7 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t) * insn itself is trapped, then detect the case with the help of * invalid fault code which is being set in arch_uprobe_pre_xol */ - if (t->thread.fault_code != UPROBE_INV_FAULT_CODE) - return true; - - return false; + return t->thread.fault_code != UPROBE_INV_FAULT_CODE; } bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) From 35c3dcb1ac2c7e347b38f3b0bccb0a2d1dbda25c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 1 Dec 2025 20:06:32 +0800 Subject: [PATCH 02/43] syscall.h: Remove unused SYSCALL_MAX_ARGS The "SYSCALL_MAX_ARGS" appears to have been unused since commit 32d92586629a ("syscalls: Remove start and number from syscall_set_arguments() args"), so remove it. Fixes: 32d92586629a ("syscalls: Remove start and number from syscall_set_arguments() args") Signed-off-by: Jinjie Ruan Reviewed-by: Russell King (Oracle) Signed-off-by: Will Deacon --- arch/arm/include/asm/syscall.h | 2 -- arch/arm64/include/asm/syscall.h | 2 -- arch/xtensa/include/asm/syscall.h | 1 - 3 files changed, 5 deletions(-) diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h index 18b102a30741..574bbcc55382 100644 --- a/arch/arm/include/asm/syscall.h +++ b/arch/arm/include/asm/syscall.h @@ -92,8 +92,6 @@ static inline void syscall_set_nr(struct task_struct *task, (nr & __NR_SYSCALL_MASK); } -#define SYSCALL_MAX_ARGS 7 - static inline void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned long *args) diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 712daa90e643..f3853047c28e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -77,8 +77,6 @@ static inline void syscall_set_nr(struct task_struct *task, } } -#define SYSCALL_MAX_ARGS 6 - static inline void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned long *args) diff --git a/arch/xtensa/include/asm/syscall.h b/arch/xtensa/include/asm/syscall.h index 7db3b489c8ad..bab7cdd96cbe 100644 --- a/arch/xtensa/include/asm/syscall.h +++ b/arch/xtensa/include/asm/syscall.h @@ -61,7 +61,6 @@ static inline void syscall_set_return_value(struct task_struct *task, regs->areg[2] = (long) error ? error : val; } -#define SYSCALL_MAX_ARGS 6 #define XTENSA_SYSCALL_ARGUMENT_REGS {6, 3, 4, 5, 8, 9} static inline void syscall_get_arguments(struct task_struct *task, From 98cc091262effcdbdeb1c2ee0a25981aefd2274c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 1 Dec 2025 20:06:33 +0800 Subject: [PATCH 03/43] arm64: Avoid memcpy() for syscall_get_arguments() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not use memcpy() to extract syscall arguments from struct pt_regs but rather just perform direct assignments. Update syscall_set_arguments() too to keep syscall_get_arguments() and syscall_set_arguments() in sync. With Generic Entry patch[1] and turn on audit, the performance benchmarks from perf bench basic syscall on kunpeng920 gives roughly a 1% performance uplift. | Metric | W/O this patch | With this patch | Change | | ---------- | -------------- | --------------- | --------- | | Total time | 2.241 [sec] | 2.211 [sec] | ↓1.36% | | usecs/op | 0.224157 | 0.221146 | ↓1.36% | | ops/sec | 4,461,157 | 4,501,409 | ↑0.9% | Disassembly shows that using direct assignment causes syscall_set_arguments() to be inlined and cuts the instruction count by five or six compared to memcpy(). Because __audit_syscall_entry() only uses four syscall arguments, the compiler has also elided the copy of regs->regs[4] and regs->regs[5]. Before: : aa0103e2 mov x2, x1 91002003 add x3, x0, #0x8 f9408804 ldr x4, [x0, #272] f8008444 str x4, [x2], #8 a9409404 ldp x4, x5, [x0, #8] a9009424 stp x4, x5, [x1, #8] a9418400 ldp x0, x1, [x0, #24] a9010440 stp x0, x1, [x2, #16] f9401060 ldr x0, [x3, #32] f9001040 str x0, [x2, #32] d65f03c0 ret d503201f nop After: a9408e82 ldp x2, x3, [x20, #8] 2a1603e0 mov w0, w22 f9400e84 ldr x4, [x20, #24] f9408a81 ldr x1, [x20, #272] 9401c4ba bl ffff800080215ca8 <__audit_syscall_entry> This also aligns the implementation with x86 and RISC-V. [1]: https://lore.kernel.org/all/20251126071446.3234218-1-ruanjinjie@huawei.com/ Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon --- arch/arm64/include/asm/syscall.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index f3853047c28e..5e4c7fc44f73 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -82,16 +82,24 @@ static inline void syscall_get_arguments(struct task_struct *task, unsigned long *args) { args[0] = regs->orig_x0; - args++; - - memcpy(args, ®s->regs[1], 5 * sizeof(args[0])); + args[1] = regs->regs[1]; + args[2] = regs->regs[2]; + args[3] = regs->regs[3]; + args[4] = regs->regs[4]; + args[5] = regs->regs[5]; } static inline void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, const unsigned long *args) { - memcpy(®s->regs[0], args, 6 * sizeof(args[0])); + regs->regs[0] = args[0]; + regs->regs[1] = args[1]; + regs->regs[2] = args[2]; + regs->regs[3] = args[3]; + regs->regs[4] = args[4]; + regs->regs[5] = args[5]; + /* * Also copy the first argument into orig_x0 * so that syscall_get_arguments() would return it From 12a94953c37e834c3eabb839ce057094946fe67a Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 24 Nov 2025 16:39:54 +0000 Subject: [PATCH 04/43] perf/arm-cmn: Support CMN-600AE The functional safety features of CMN-600AE have little to no impact on the PMU relative to the base CMN-600 design, so for simplicity we can reasonably just treat it as the same thing. The only obvious difference is that the revision numbers aren't aligned, so we may hide some aliases for events which do actually exist, but those can still be specified via the underlying "type,eventid" format so it's not too big a deal. Signed-off-by: Robin Murphy Reviewed-by: Ilkka Koskinen Tested-by: Michal Simek Signed-off-by: Will Deacon --- drivers/perf/arm-cmn.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 23245352a3fc..651edd73bfcb 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -210,6 +210,7 @@ enum cmn_model { enum cmn_part { PART_CMN600 = 0x434, PART_CMN650 = 0x436, + PART_CMN600AE = 0x438, PART_CMN700 = 0x43c, PART_CI700 = 0x43a, PART_CMN_S3 = 0x43e, @@ -2266,6 +2267,9 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) reg = readq_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_01); part = FIELD_GET(CMN_CFGM_PID0_PART_0, reg); part |= FIELD_GET(CMN_CFGM_PID1_PART_1, reg) << 8; + /* 600AE is close enough that it's not really worth more complexity */ + if (part == PART_CMN600AE) + part = PART_CMN600; if (cmn->part && cmn->part != part) dev_warn(cmn->dev, "Firmware binding mismatch: expected part number 0x%x, found 0x%x\n", From 0c7c64146f707ffe7abd5d11c9828d8129903ab5 Mon Sep 17 00:00:00 2001 From: "Yury Norov (NVIDIA)" Date: Mon, 15 Dec 2025 20:20:03 -0500 Subject: [PATCH 05/43] drivers: perf: use bitmap_empty() where appropriate bitmap_empty() is more verbose and efficient, as it stops traversing bitmaps as soon as the 1st set bit found. Switch perf code to using bitmap_empty() where appropriate, and correspondingly use boolean types. Signed-off-by: Yury Norov (NVIDIA) Signed-off-by: Will Deacon --- drivers/perf/riscv_pmu_sbi.c | 2 +- drivers/perf/starfive_starlink_pmu.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 7dd282da67ce..9dcc22fd48ef 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -1244,7 +1244,7 @@ static int riscv_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, { struct riscv_pmu *rvpmu = container_of(b, struct riscv_pmu, riscv_pm_nb); struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events); - int enabled = bitmap_weight(cpuc->used_hw_ctrs, RISCV_MAX_COUNTERS); + bool enabled = !bitmap_empty(cpuc->used_hw_ctrs, RISCV_MAX_COUNTERS); struct perf_event *event; int idx; diff --git a/drivers/perf/starfive_starlink_pmu.c b/drivers/perf/starfive_starlink_pmu.c index 5e5a672b4229..964897c2baa9 100644 --- a/drivers/perf/starfive_starlink_pmu.c +++ b/drivers/perf/starfive_starlink_pmu.c @@ -450,8 +450,7 @@ static int starlink_pmu_pm_notify(struct notifier_block *b, starlink_pmu_pm_nb); struct starlink_hw_events *hw_events = this_cpu_ptr(starlink_pmu->hw_events); - int enabled = bitmap_weight(hw_events->used_mask, - STARLINK_PMU_MAX_COUNTERS); + bool enabled = !bitmap_empty(hw_events->used_mask, STARLINK_PMU_MAX_COUNTERS); struct perf_event *event; int idx; From e97e3e31d898833b2396310056cbf82e23956b36 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Fri, 19 Dec 2025 22:49:06 +0800 Subject: [PATCH 06/43] MAINTAINERS: Update the maintainers for ARM64 ACPI ARM64 ACPI patches go via ARM64 tree, but the listed maintainers for ARM64 ACPI don't include Catalin and Will, and there is no F: line under the ARM64 architecture entry, so emails will not route to Catalin and Will in an automatic way. Adding Catalin and Will to the maintainers entry for ARM64 ACPI to fix the problem, it will make life easy for merging ARM64 ACPI patches. Adding new maintainer entries for ARM64 ACPI doesn't mean the review work will balance to the new maintainer, patches still need to be acked by currently listed folks (Lorenzo, Hanjun, Sudeep) before merging them. Link: https://lore.kernel.org/linux-acpi/aS2ZTfS9YVO98Exe@willie-the-truck/ Signed-off-by: Hanjun Guo Acked-by: Lorenzo Pieralisi Signed-off-by: Will Deacon --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..6050d94a50ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -335,6 +335,8 @@ ACPI FOR ARM64 (ACPI/arm64) M: Lorenzo Pieralisi M: Hanjun Guo M: Sudeep Holla +M: Catalin Marinas +M: Will Deacon L: linux-acpi@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained From e3baa5d4b361276efeb87b20d8beced451a7dbd5 Mon Sep 17 00:00:00 2001 From: Jinqian Yang Date: Sat, 27 Dec 2025 17:24:48 +0800 Subject: [PATCH 07/43] arm64: Add support for TSV110 Spectre-BHB mitigation The TSV110 processor is vulnerable to the Spectre-BHB (Branch History Buffer) attack, which can be exploited to leak information through branch prediction side channels. This commit adds the MIDR of TSV110 to the list for software mitigation. Signed-off-by: Jinqian Yang Reviewed-by: Zenghui Yu Signed-off-by: Will Deacon --- arch/arm64/kernel/proton-pack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c index 80a580e019c5..b3801f532b10 100644 --- a/arch/arm64/kernel/proton-pack.c +++ b/arch/arm64/kernel/proton-pack.c @@ -887,6 +887,7 @@ static u8 spectre_bhb_loop_affected(void) MIDR_ALL_VERSIONS(MIDR_CORTEX_X2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), + MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), {}, }; static const struct midr_range spectre_bhb_k24_list[] = { From af15bc259e260cbe5221c220d56a716b6facf155 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Wed, 19 Nov 2025 16:13:54 +0800 Subject: [PATCH 08/43] arm64: topology: Skip already covered CPUs when setting freq source The scale freq source of the CPUs in 'amu_fie_cpus' mask are already set to AMU tick before, so in amu_fie_setup(), only the CPUs in the 'cpus' mask should be set. Signed-off-by: Lifeng Zheng Reviewed-by: Beata Michalska Reviewed-by: Jie Zhan Acked-by: Beata Michalska Signed-off-by: Will Deacon --- arch/arm64/kernel/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 5d24dc53799b..cf9bb761af3a 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -272,7 +272,7 @@ static void amu_fie_setup(const struct cpumask *cpus) cpumask_or(amu_fie_cpus, amu_fie_cpus, cpus); - topology_set_scale_freq_source(&amu_sfd, amu_fie_cpus); + topology_set_scale_freq_source(&amu_sfd, cpus); pr_debug("CPUs[%*pbl]: counters will be used for FIE.", cpumask_pr_args(cpus)); From 4221504c4328d4d9e57962bf530fa52913591139 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Wed, 19 Nov 2025 16:13:55 +0800 Subject: [PATCH 09/43] cpufreq: Add new helper function returning cpufreq policy cpufreq_cpu_get_raw() gets cpufreq policy only if the CPU is in policy->cpus mask, which means the CPU is already online. But in some cases, the policy is needed before the CPU is added to cpus mask. Add a function to get the policy in these cases. Signed-off-by: Lifeng Zheng Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Jie Zhan Acked-by: Beata Michalska Signed-off-by: Will Deacon --- drivers/cpufreq/cpufreq.c | 6 ++++++ include/linux/cpufreq.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 4472bb1ec83c..076c8eb5db37 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -198,6 +198,12 @@ struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_cpu_get_raw); +struct cpufreq_policy *cpufreq_cpu_policy(unsigned int cpu) +{ + return per_cpu(cpufreq_cpu_data, cpu); +} +EXPORT_SYMBOL_GPL(cpufreq_cpu_policy); + unsigned int cpufreq_generic_get(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 0465d1e6f72a..cc894fc38971 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -203,6 +203,7 @@ struct cpufreq_freqs { #ifdef CONFIG_CPU_FREQ struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu); +struct cpufreq_policy *cpufreq_cpu_policy(unsigned int cpu); struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); void cpufreq_cpu_put(struct cpufreq_policy *policy); #else @@ -210,6 +211,10 @@ static inline struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) { return NULL; } +static inline struct cpufreq_policy *cpufreq_cpu_policy(unsigned int cpu) +{ + return NULL; +} static inline struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { return NULL; From 6fd9be0b7b2e957d24b0490b989658c4d18d92c7 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Wed, 19 Nov 2025 16:13:56 +0800 Subject: [PATCH 10/43] arm64: topology: Handle AMU FIE setup on CPU hotplug Currently, when a cpufreq policy is created, the AMU FIE setup process checks all CPUs in the policy -- including those that are offline. If any of these CPUs are offline at that time, their AMU capability flag hasn't been verified yet, leading the check fail. As a result, AMU FIE is not enabled, even if the CPUs that are online do support it. Later, when the previously offline CPUs come online and report AMU support, there's no mechanism in place to re-enable AMU FIE for the policy. This leaves the entire frequency domain without AMU FIE, despite being eligible. Restrict the initial AMU FIE check to only those CPUs that are online at the time the policy is created, and allow CPUs that come online later to join the policy with AMU FIE enabled. Signed-off-by: Lifeng Zheng Acked-by: Beata Michalska Signed-off-by: Will Deacon --- arch/arm64/kernel/topology.c | 65 ++++++++++++++++++++++++++++++++++-- drivers/base/arch_topology.c | 9 ++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index cf9bb761af3a..539b38935182 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -284,7 +284,7 @@ static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, struct cpufreq_policy *policy = data; if (val == CPUFREQ_CREATE_POLICY) - amu_fie_setup(policy->related_cpus); + amu_fie_setup(policy->cpus); /* * We don't need to handle CPUFREQ_REMOVE_POLICY event as the AMU @@ -303,10 +303,71 @@ static struct notifier_block init_amu_fie_notifier = { .notifier_call = init_amu_fie_callback, }; +static int cpuhp_topology_online(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_policy(cpu); + + /* Those are cheap checks */ + + /* + * Skip this CPU if: + * - it has no cpufreq policy assigned yet, + * - no policy exists that spans CPUs with AMU counters, or + * - it was already handled. + */ + if (unlikely(!policy) || !cpumask_available(amu_fie_cpus) || + cpumask_test_cpu(cpu, amu_fie_cpus)) + return 0; + + /* + * Only proceed if all already-online CPUs in this policy + * support AMU counters. + */ + if (unlikely(!cpumask_subset(policy->cpus, amu_fie_cpus))) + return 0; + + /* + * If the new online CPU cannot pass this check, all the CPUs related to + * the same policy should be clear from amu_fie_cpus mask, otherwise they + * may use different source of the freq scale. + */ + if (!freq_counters_valid(cpu)) { + pr_warn("CPU[%u] doesn't support AMU counters\n", cpu); + topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_ARCH, + policy->related_cpus); + cpumask_andnot(amu_fie_cpus, amu_fie_cpus, policy->related_cpus); + return 0; + } + + cpumask_set_cpu(cpu, amu_fie_cpus); + + topology_set_scale_freq_source(&amu_sfd, cpumask_of(cpu)); + + pr_debug("CPU[%u]: counter will be used for FIE.", cpu); + + return 0; +} + static int __init init_amu_fie(void) { - return cpufreq_register_notifier(&init_amu_fie_notifier, + int ret; + + ret = cpufreq_register_notifier(&init_amu_fie_notifier, CPUFREQ_POLICY_NOTIFIER); + if (ret) + return ret; + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + "arm64/topology:online", + cpuhp_topology_online, + NULL); + if (ret < 0) { + cpufreq_unregister_notifier(&init_amu_fie_notifier, + CPUFREQ_POLICY_NOTIFIER); + return ret; + } + + return 0; } core_initcall(init_amu_fie); diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 84ec92bff642..c0ef6ea9c111 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -34,7 +34,14 @@ EXPORT_PER_CPU_SYMBOL_GPL(capacity_freq_ref); static bool supports_scale_freq_counters(const struct cpumask *cpus) { - return cpumask_subset(cpus, &scale_freq_counters_mask); + int i; + + for_each_cpu(i, cpus) { + if (cpumask_test_cpu(i, &scale_freq_counters_mask)) + return true; + } + + return false; } bool topology_scale_freq_invariant(void) From fb36d71308a770268c771d6697f22615e5ddbd6e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 19 Dec 2025 15:29:42 +0000 Subject: [PATCH 11/43] kselftest/arm64: Support FORCE_TARGETS The top level kselftest Makefile supports an option FORCE_TARGETS which causes any failures during the build to be propagated to the exit status of the top level make, useful during build testing. Currently the recursion done by the arm64 selftests ignores this option, meaning arm64 failures are not reported via this mechanism. Add the logic to implement FORCE_TARGETS so that it works for arm64. Signed-off-by: Mark Brown Acked-by: Shuah Khan Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index c4c72ee2ef55..e456f3b62fa1 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -30,13 +30,15 @@ all: @for DIR in $(ARM64_SUBTARGETS); do \ BUILD_TARGET=$(OUTPUT)/$$DIR; \ mkdir -p $$BUILD_TARGET; \ - make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@ \ + $(if $(FORCE_TARGETS),|| exit); \ done install: all @for DIR in $(ARM64_SUBTARGETS); do \ BUILD_TARGET=$(OUTPUT)/$$DIR; \ - make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@ \ + $(if $(FORCE_TARGETS),|| exit); \ done run_tests: all From 15dd20dda979ebab72f6df97845828e78d63ab91 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Mon, 1 Dec 2025 10:51:18 +0000 Subject: [PATCH 12/43] arm64: kernel: initialize missing kexec_buf->random field Commit bf454ec31add ("kexec_file: allow to place kexec_buf randomly") introduced the kexec_buf->random field to enable random placement of kexec_buf. However, this field was never properly initialized for kexec images that do not need to be placed randomly, leading to the following UBSAN warning: [ +0.364528] ------------[ cut here ]------------ [ +0.000019] UBSAN: invalid-load in ./include/linux/kexec.h:210:12 [ +0.000131] load of value 2 is not a valid value for type 'bool' (aka '_Bool') [ +0.000003] CPU: 4 UID: 0 PID: 927 Comm: kexec Not tainted 6.18.0-rc7+ #3 PREEMPT(full) [ +0.000002] Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 [ +0.000000] Call trace: [ +0.000001] show_stack+0x24/0x40 (C) [ +0.000006] __dump_stack+0x28/0x48 [ +0.000002] dump_stack_lvl+0x7c/0xb0 [ +0.000002] dump_stack+0x18/0x34 [ +0.000001] ubsan_epilogue+0x10/0x50 [ +0.000002] __ubsan_handle_load_invalid_value+0xc8/0xd0 [ +0.000003] locate_mem_hole_callback+0x28c/0x2a0 [ +0.000003] kexec_locate_mem_hole+0xf4/0x2f0 [ +0.000001] kexec_add_buffer+0xa8/0x178 [ +0.000002] image_load+0xf0/0x258 [ +0.000001] __arm64_sys_kexec_file_load+0x510/0x718 [ +0.000002] invoke_syscall+0x68/0xe8 [ +0.000001] el0_svc_common+0xb0/0xf8 [ +0.000002] do_el0_svc+0x28/0x48 [ +0.000001] el0_svc+0x40/0xe8 [ +0.000002] el0t_64_sync_handler+0x84/0x140 [ +0.000002] el0t_64_sync+0x1bc/0x1c0 To address this, initialise kexec_buf->random field properly. Fixes: bf454ec31add ("kexec_file: allow to place kexec_buf randomly") Suggested-by: Breno Leitao Cc: stable@vger.kernel.org Signed-off-by: Yeoreum Yun Reviewed-by: Breno Leitao Link: https://lore.kernel.org/all/oninomspajhxp4omtdapxnckxydbk2nzmrix7rggmpukpnzadw@c67o7njgdgm3/ [1] Link: https://lore.kernel.org/all/20250825180531.94bfb86a26a43127c0a1296f@linux-foundation.org/ [2] Link: https://lkml.kernel.org/r/20250826-akpm-v1-1-3c831f0e3799@debian.org Signed-off-by: Breno Leitao Suggested-by: Andrew Morton Signed-off-by: Andrew Morton Reviewed-by: Pratyush Yadav Signed-off-by: Will Deacon --- arch/arm64/kernel/kexec_image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/kexec_image.c b/arch/arm64/kernel/kexec_image.c index 532d72ea42ee..b70f4df15a1a 100644 --- a/arch/arm64/kernel/kexec_image.c +++ b/arch/arm64/kernel/kexec_image.c @@ -41,7 +41,7 @@ static void *image_load(struct kimage *image, struct arm64_image_header *h; u64 flags, value; bool be_image, be_kernel; - struct kexec_buf kbuf; + struct kexec_buf kbuf = {}; unsigned long text_offset, kernel_segment_number; struct kexec_segment *kernel_segment; int ret; From 0113affc91014a14251890c3af8f2bade1c20222 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 15 Dec 2025 13:04:58 +0000 Subject: [PATCH 13/43] perf/arm_dsu: Support DSU-110 DSU-110 sneakily made all the event counters 64-bit, perhaps related to no longer having AArch32 EL1 to worry about. While the DSU version itself is not easily discoverable, the size of a counter certainly is. Signed-off-by: Robin Murphy Signed-off-by: Will Deacon --- drivers/perf/arm_dsu_pmu.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index cb4fb59fe04b..8663721ee018 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -66,13 +66,6 @@ */ #define DSU_PMU_IDX_CYCLE_COUNTER 31 -/* All event counters are 32bit, with a 64bit Cycle counter */ -#define DSU_PMU_COUNTER_WIDTH(idx) \ - (((idx) == DSU_PMU_IDX_CYCLE_COUNTER) ? 64 : 32) - -#define DSU_PMU_COUNTER_MASK(idx) \ - GENMASK_ULL((DSU_PMU_COUNTER_WIDTH((idx)) - 1), 0) - #define DSU_EXT_ATTR(_name, _func, _config) \ (&((struct dev_ext_attribute[]) { \ { \ @@ -107,6 +100,7 @@ struct dsu_hw_events { * @num_counters : Number of event counters implemented by the PMU, * excluding the cycle counter. * @irq : Interrupt line for counter overflow. + * @has_32b_pmevcntr : Are the non-cycle counters only 32-bit? * @cpmceid_bitmap : Bitmap for the availability of architected common * events (event_code < 0x40). */ @@ -120,6 +114,7 @@ struct dsu_pmu { struct hlist_node cpuhp_node; s8 num_counters; int irq; + bool has_32b_pmevcntr; DECLARE_BITMAP(cpmceid_bitmap, DSU_PMU_MAX_COMMON_EVENTS); }; @@ -328,6 +323,11 @@ static inline void dsu_pmu_set_event(struct dsu_pmu *dsu_pmu, raw_spin_unlock_irqrestore(&dsu_pmu->pmu_lock, flags); } +static u64 dsu_pmu_counter_mask(struct hw_perf_event *hw) +{ + return (hw->flags && hw->idx != DSU_PMU_IDX_CYCLE_COUNTER) ? U32_MAX : U64_MAX; +} + static void dsu_pmu_event_update(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -339,7 +339,7 @@ static void dsu_pmu_event_update(struct perf_event *event) new_count = dsu_pmu_read_counter(event); } while (local64_cmpxchg(&hwc->prev_count, prev_count, new_count) != prev_count); - delta = (new_count - prev_count) & DSU_PMU_COUNTER_MASK(hwc->idx); + delta = (new_count - prev_count) & dsu_pmu_counter_mask(hwc); local64_add(delta, &event->count); } @@ -362,8 +362,7 @@ static inline u32 dsu_pmu_get_reset_overflow(void) */ static void dsu_pmu_set_event_period(struct perf_event *event) { - int idx = event->hw.idx; - u64 val = DSU_PMU_COUNTER_MASK(idx) >> 1; + u64 val = dsu_pmu_counter_mask(&event->hw) >> 1; local64_set(&event->hw.prev_count, val); dsu_pmu_write_counter(event, val); @@ -564,6 +563,7 @@ static int dsu_pmu_event_init(struct perf_event *event) return -EINVAL; event->hw.config_base = event->attr.config; + event->hw.flags = dsu_pmu->has_32b_pmevcntr; return 0; } @@ -664,6 +664,10 @@ static void dsu_pmu_probe_pmu(struct dsu_pmu *dsu_pmu) cpmceid[1] = __dsu_pmu_read_pmceid(1); bitmap_from_arr32(dsu_pmu->cpmceid_bitmap, cpmceid, DSU_PMU_MAX_COMMON_EVENTS); + /* Newer DSUs have 64-bit counters */ + __dsu_pmu_write_counter(0, U64_MAX); + if (__dsu_pmu_read_counter(0) != U64_MAX) + dsu_pmu->has_32b_pmevcntr = true; } static void dsu_pmu_set_active_cpu(int cpu, struct dsu_pmu *dsu_pmu) From 85c0dbd8b6e2ca5e672560c7cd86801bffa0d884 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 15 Dec 2025 13:04:59 +0000 Subject: [PATCH 14/43] perf/arm_dsu: Support DSU-120 DSU-120 has the same system register interface as previous DSUs, but no longer offers a dedicated cycle counter. While this is not directly discoverable via PMCR, the PMCCNTR register is still defined to exist with RAZ/WI behaviour, allowing for a straightforward heuristic. Signed-off-by: Robin Murphy Signed-off-by: Will Deacon --- drivers/perf/arm_dsu_pmu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index 8663721ee018..56c592f0dae3 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -101,6 +101,7 @@ struct dsu_hw_events { * excluding the cycle counter. * @irq : Interrupt line for counter overflow. * @has_32b_pmevcntr : Are the non-cycle counters only 32-bit? + * @has_pmccntr : Do we even have a dedicated cycle counter? * @cpmceid_bitmap : Bitmap for the availability of architected common * events (event_code < 0x40). */ @@ -115,6 +116,7 @@ struct dsu_pmu { s8 num_counters; int irq; bool has_32b_pmevcntr; + bool has_pmccntr; DECLARE_BITMAP(cpmceid_bitmap, DSU_PMU_MAX_COMMON_EVENTS); }; @@ -281,7 +283,7 @@ static int dsu_pmu_get_event_idx(struct dsu_hw_events *hw_events, struct dsu_pmu *dsu_pmu = to_dsu_pmu(event->pmu); unsigned long *used_mask = hw_events->used_mask; - if (evtype == DSU_PMU_EVT_CYCLES) { + if (evtype == DSU_PMU_EVT_CYCLES && dsu_pmu->has_pmccntr) { if (test_and_set_bit(DSU_PMU_IDX_CYCLE_COUNTER, used_mask)) return -EAGAIN; return DSU_PMU_IDX_CYCLE_COUNTER; @@ -668,6 +670,10 @@ static void dsu_pmu_probe_pmu(struct dsu_pmu *dsu_pmu) __dsu_pmu_write_counter(0, U64_MAX); if (__dsu_pmu_read_counter(0) != U64_MAX) dsu_pmu->has_32b_pmevcntr = true; + /* On even newer DSUs, PMCCNTR is RAZ/WI */ + __dsu_pmu_write_pmccntr(U64_MAX); + if (__dsu_pmu_read_pmccntr() == U64_MAX) + dsu_pmu->has_pmccntr = true; } static void dsu_pmu_set_active_cpu(int cpu, struct dsu_pmu *dsu_pmu) From 79448fa1f495c3e3b7119e53bedc3cce273aa95f Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 15 Dec 2025 13:05:00 +0000 Subject: [PATCH 15/43] perf/arm_dsu: Allow standard cycles events Since we do not use the divide-by-64 option, there should be no significant difference between the dedicated cycle counter and the standard cycles event. Since using the latter on DSU-120 now has the side-effect of allowing multiple cycles events to be scheduled simultaneously (beneficial for multiple cycle-based metrics), there seems little reason not to allow the same on older DSUs as well. Signed-off-by: Robin Murphy Signed-off-by: Will Deacon --- drivers/perf/arm_dsu_pmu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index 56c592f0dae3..32b0dd7c693b 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -284,9 +284,8 @@ static int dsu_pmu_get_event_idx(struct dsu_hw_events *hw_events, unsigned long *used_mask = hw_events->used_mask; if (evtype == DSU_PMU_EVT_CYCLES && dsu_pmu->has_pmccntr) { - if (test_and_set_bit(DSU_PMU_IDX_CYCLE_COUNTER, used_mask)) - return -EAGAIN; - return DSU_PMU_IDX_CYCLE_COUNTER; + if (!test_and_set_bit(DSU_PMU_IDX_CYCLE_COUNTER, used_mask)) + return DSU_PMU_IDX_CYCLE_COUNTER; } idx = find_first_zero_bit(used_mask, dsu_pmu->num_counters); From f22c81bebf8bda6e54dc132df0ed54f6bf8756f9 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 6 Jan 2026 02:16:35 -0800 Subject: [PATCH 16/43] arm64: Disable branch profiling for all arm64 code The arm64 kernel doesn't boot with annotated branches (PROFILE_ANNOTATED_BRANCHES) enabled and CONFIG_DEBUG_VIRTUAL together. Bisecting it, I found that disabling branch profiling in arch/arm64/mm solved the problem. Narrowing down a bit further, I found that physaddr.c is the file that needs to have branch profiling disabled to get the machine to boot. I suspect that it might invoke some ftrace helper very early in the boot process and ftrace is still not enabled(!?). Rather than playing whack-a-mole with individual files, disable branch profiling for the entire arch/arm64 tree, similar to what x86 already does in arch/x86/Kbuild. Cc: stable@vger.kernel.org Signed-off-by: Breno Leitao Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/Kbuild | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/Kbuild b/arch/arm64/Kbuild index 5bfbf7d79c99..d876bc0e5421 100644 --- a/arch/arm64/Kbuild +++ b/arch/arm64/Kbuild @@ -1,4 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only + +# Branch profiling isn't noinstr-safe +subdir-ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING + obj-y += kernel/ mm/ net/ obj-$(CONFIG_KVM) += kvm/ obj-$(CONFIG_XEN) += xen/ From f7d5e9e70302c06c04599919770f3e0cb339342e Mon Sep 17 00:00:00 2001 From: Kazuhiro Abe Date: Thu, 8 Jan 2026 16:56:09 +0900 Subject: [PATCH 17/43] ACPI: AGDI: Add interrupt signaling mode support AGDI has two types of signaling modes: SDEI and interrupt. Currently, the AGDI driver only supports SDEI. Therefore, add support for interrupt signaling mode. The interrupt vector is retrieved from the AGDI table, and call panic function when an interrupt occurs. Acked-by: Hanjun Guo Reviewed-by: Ilkka Koskinen Signed-off-by: Kazuhiro Abe Signed-off-by: Will Deacon --- drivers/acpi/arm64/agdi.c | 101 ++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c index e0df3daa4abf..feb4b2cb4618 100644 --- a/drivers/acpi/arm64/agdi.c +++ b/drivers/acpi/arm64/agdi.c @@ -16,7 +16,11 @@ #include "init.h" struct agdi_data { + unsigned char flags; /* AGDI Signaling Mode */ int sdei_event; + unsigned int gsiv; + bool use_nmi; + int irq; }; static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg) @@ -48,6 +52,57 @@ static int agdi_sdei_probe(struct platform_device *pdev, return 0; } +static irqreturn_t agdi_interrupt_handler_nmi(int irq, void *dev_id) +{ + nmi_panic(NULL, "Arm Generic Diagnostic Dump and Reset NMI Interrupt event issued\n"); + return IRQ_HANDLED; +} + +static irqreturn_t agdi_interrupt_handler_irq(int irq, void *dev_id) +{ + panic("Arm Generic Diagnostic Dump and Reset Interrupt event issued\n"); + return IRQ_HANDLED; +} + +static int agdi_interrupt_probe(struct platform_device *pdev, + struct agdi_data *adata) +{ + unsigned long irq_flags; + int ret; + int irq; + + irq = acpi_register_gsi(NULL, adata->gsiv, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH); + if (irq < 0) { + dev_err(&pdev->dev, "cannot register GSI#%d (%d)\n", adata->gsiv, irq); + return irq; + } + + irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN | + IRQF_NO_THREAD; + /* try NMI first */ + ret = request_nmi(irq, &agdi_interrupt_handler_nmi, irq_flags, + "agdi_interrupt_nmi", NULL); + if (!ret) { + enable_nmi(irq); + adata->irq = irq; + adata->use_nmi = true; + return 0; + } + + /* Then try normal interrupt */ + ret = request_irq(irq, &agdi_interrupt_handler_irq, + irq_flags, "agdi_interrupt_irq", NULL); + if (ret) { + dev_err(&pdev->dev, "cannot register IRQ %d\n", ret); + acpi_unregister_gsi(adata->gsiv); + return ret; + } + enable_irq(irq); + adata->irq = irq; + + return 0; +} + static int agdi_probe(struct platform_device *pdev) { struct agdi_data *adata = dev_get_platdata(&pdev->dev); @@ -55,12 +110,15 @@ static int agdi_probe(struct platform_device *pdev) if (!adata) return -EINVAL; - return agdi_sdei_probe(pdev, adata); + if (adata->flags & ACPI_AGDI_SIGNALING_MODE) + return agdi_interrupt_probe(pdev, adata); + else + return agdi_sdei_probe(pdev, adata); } -static void agdi_remove(struct platform_device *pdev) +static void agdi_sdei_remove(struct platform_device *pdev, + struct agdi_data *adata) { - struct agdi_data *adata = dev_get_platdata(&pdev->dev); int err, i; err = sdei_event_disable(adata->sdei_event); @@ -83,6 +141,30 @@ static void agdi_remove(struct platform_device *pdev) adata->sdei_event, ERR_PTR(err)); } +static void agdi_interrupt_remove(struct platform_device *pdev, + struct agdi_data *adata) +{ + if (adata->irq == -1) + return; + + if (adata->use_nmi) + free_nmi(adata->irq, NULL); + else + free_irq(adata->irq, NULL); + + acpi_unregister_gsi(adata->gsiv); +} + +static void agdi_remove(struct platform_device *pdev) +{ + struct agdi_data *adata = dev_get_platdata(&pdev->dev); + + if (adata->flags & ACPI_AGDI_SIGNALING_MODE) + agdi_interrupt_remove(pdev, adata); + else + agdi_sdei_remove(pdev, adata); +} + static struct platform_driver agdi_driver = { .driver = { .name = "agdi", @@ -94,7 +176,7 @@ static struct platform_driver agdi_driver = { void __init acpi_agdi_init(void) { struct acpi_table_agdi *agdi_table; - struct agdi_data pdata; + struct agdi_data pdata = { 0 }; struct platform_device *pdev; acpi_status status; @@ -103,12 +185,13 @@ void __init acpi_agdi_init(void) if (ACPI_FAILURE(status)) return; - if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) { - pr_warn("Interrupt signaling is not supported"); - goto err_put_table; - } + if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) + pdata.gsiv = agdi_table->gsiv; + else + pdata.sdei_event = agdi_table->sdei_event; - pdata.sdei_event = agdi_table->sdei_event; + pdata.irq = -1; + pdata.flags = agdi_table->flags; pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata)); if (IS_ERR(pdev)) From 6191b25d8bd902c1a107170df54bddbb0e2335f8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 7 Jan 2026 18:06:59 +0000 Subject: [PATCH 18/43] arm64: Unconditionally enable LSE support LSE atomics have been in the architecture since ARMv8.1 (released in 2014), and are hopefully supported by all modern toolchains. Drop the optional nature of LSE support in the kernel, and always compile the support in, as this really is very little code. LL/SC still is the default, and the switch to LSE is done dynamically. Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon --- arch/arm64/Kconfig | 16 ---------------- arch/arm64/include/asm/insn.h | 23 ----------------------- arch/arm64/include/asm/lse.h | 9 --------- arch/arm64/kernel/cpufeature.c | 2 -- arch/arm64/kvm/at.c | 7 ------- arch/arm64/lib/insn.c | 2 -- arch/arm64/net/bpf_jit_comp.c | 7 ------- 7 files changed, 66 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 93173f0a09c7..b6f57cc1e4df 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1873,22 +1873,6 @@ config ARM64_PAN The feature is detected at runtime, and will remain as a 'nop' instruction if the cpu does not implement the feature. -config ARM64_LSE_ATOMICS - bool - default ARM64_USE_LSE_ATOMICS - -config ARM64_USE_LSE_ATOMICS - bool "Atomic instructions" - default y - help - As part of the Large System Extensions, ARMv8.1 introduces new - atomic instructions that are designed specifically to scale in - very large systems. - - Say Y here to make use of these instructions for the in-kernel - atomic routines. This incurs a small overhead on CPUs that do - not support these instructions. - endmenu # "ARMv8.1 architectural features" menu "ARMv8.2 architectural features" diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index e1d30ba99d01..f463a654a2bb 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -671,7 +671,6 @@ u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant, enum aarch64_insn_register Rn, enum aarch64_insn_register Rd, u8 lsb); -#ifdef CONFIG_ARM64_LSE_ATOMICS u32 aarch64_insn_gen_atomic_ld_op(enum aarch64_insn_register result, enum aarch64_insn_register address, enum aarch64_insn_register value, @@ -683,28 +682,6 @@ u32 aarch64_insn_gen_cas(enum aarch64_insn_register result, enum aarch64_insn_register value, enum aarch64_insn_size_type size, enum aarch64_insn_mem_order_type order); -#else -static inline -u32 aarch64_insn_gen_atomic_ld_op(enum aarch64_insn_register result, - enum aarch64_insn_register address, - enum aarch64_insn_register value, - enum aarch64_insn_size_type size, - enum aarch64_insn_mem_atomic_op op, - enum aarch64_insn_mem_order_type order) -{ - return AARCH64_BREAK_FAULT; -} - -static inline -u32 aarch64_insn_gen_cas(enum aarch64_insn_register result, - enum aarch64_insn_register address, - enum aarch64_insn_register value, - enum aarch64_insn_size_type size, - enum aarch64_insn_mem_order_type order) -{ - return AARCH64_BREAK_FAULT; -} -#endif u32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type); u32 aarch64_insn_gen_dsb(enum aarch64_insn_mb_type type); u32 aarch64_insn_gen_mrs(enum aarch64_insn_register result, diff --git a/arch/arm64/include/asm/lse.h b/arch/arm64/include/asm/lse.h index 3129a5819d0e..1e77c45bb0a8 100644 --- a/arch/arm64/include/asm/lse.h +++ b/arch/arm64/include/asm/lse.h @@ -4,8 +4,6 @@ #include -#ifdef CONFIG_ARM64_LSE_ATOMICS - #define __LSE_PREAMBLE ".arch_extension lse\n" #include @@ -27,11 +25,4 @@ #define ARM64_LSE_ATOMIC_INSN(llsc, lse) \ ALTERNATIVE(llsc, __LSE_PREAMBLE lse, ARM64_HAS_LSE_ATOMICS) -#else /* CONFIG_ARM64_LSE_ATOMICS */ - -#define __lse_ll_sc_body(op, ...) __ll_sc_##op(__VA_ARGS__) - -#define ARM64_LSE_ATOMIC_INSN(llsc, lse) llsc - -#endif /* CONFIG_ARM64_LSE_ATOMICS */ #endif /* __ASM_LSE_H */ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index c840a93b9ef9..547ccf28f289 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -2560,7 +2560,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { ARM64_CPUID_FIELDS(ID_AA64MMFR1_EL1, PAN, PAN3) }, #endif /* CONFIG_ARM64_EPAN */ -#ifdef CONFIG_ARM64_LSE_ATOMICS { .desc = "LSE atomic instructions", .capability = ARM64_HAS_LSE_ATOMICS, @@ -2568,7 +2567,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64ISAR0_EL1, ATOMIC, IMP) }, -#endif /* CONFIG_ARM64_LSE_ATOMICS */ { .desc = "Virtualization Host Extensions", .capability = ARM64_HAS_VIRT_HOST_EXTN, diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 53bf70126f81..6cbcec041a9d 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1700,7 +1700,6 @@ int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, int *level) } } -#ifdef CONFIG_ARM64_LSE_ATOMICS static int __lse_swap_desc(u64 __user *ptep, u64 old, u64 new) { u64 tmp = old; @@ -1725,12 +1724,6 @@ static int __lse_swap_desc(u64 __user *ptep, u64 old, u64 new) return ret; } -#else -static int __lse_swap_desc(u64 __user *ptep, u64 old, u64 new) -{ - return -EINVAL; -} -#endif static int __llsc_swap_desc(u64 __user *ptep, u64 old, u64 new) { diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c index 4e298baddc2e..cc5b40917d0d 100644 --- a/arch/arm64/lib/insn.c +++ b/arch/arm64/lib/insn.c @@ -611,7 +611,6 @@ u32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg, state); } -#ifdef CONFIG_ARM64_LSE_ATOMICS static u32 aarch64_insn_encode_ldst_order(enum aarch64_insn_mem_order_type type, u32 insn) { @@ -755,7 +754,6 @@ u32 aarch64_insn_gen_cas(enum aarch64_insn_register result, return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn, value); } -#endif u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst, enum aarch64_insn_register src, diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 74dd29816f36..008612aa4131 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -776,7 +776,6 @@ static int emit_atomic_ld_st(const struct bpf_insn *insn, struct jit_ctx *ctx) return 0; } -#ifdef CONFIG_ARM64_LSE_ATOMICS static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) { const u8 code = insn->code; @@ -843,12 +842,6 @@ static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) return 0; } -#else -static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) -{ - return -EINVAL; -} -#endif static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) { From 018a231b0260ebd85eddca3fec85031b59f50117 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 7 Jan 2026 18:07:00 +0000 Subject: [PATCH 19/43] arm64: Unconditionally enable PAN support FEAT_PAN has been around since ARMv8.1 (over 11 years ago), has no compiler dependency (we have our own accessors), and is a great security benefit. Drop CONFIG_ARM64_PAN, and make the support unconditionnal. Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon --- arch/arm64/Kconfig | 17 ----------------- arch/arm64/include/asm/cpucaps.h | 2 -- arch/arm64/include/asm/uaccess.h | 6 ++---- arch/arm64/kernel/cpufeature.c | 4 ---- arch/arm64/kvm/hyp/entry.S | 2 +- 5 files changed, 3 insertions(+), 28 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b6f57cc1e4df..fcfb62ec4bae 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1680,7 +1680,6 @@ config MITIGATE_SPECTRE_BRANCH_HISTORY config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" depends on !KCSAN - select ARM64_PAN help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved @@ -1859,20 +1858,6 @@ config ARM64_HW_AFDBM to work on pre-ARMv8.1 hardware and the performance impact is minimal. If unsure, say Y. -config ARM64_PAN - bool "Enable support for Privileged Access Never (PAN)" - default y - help - Privileged Access Never (PAN; part of the ARMv8.1 Extensions) - prevents the kernel or hypervisor from accessing user-space (EL0) - memory directly. - - Choosing this option will cause any unprotected (not using - copy_to_user et al) memory access to fail with a permission fault. - - The feature is detected at runtime, and will remain as a 'nop' - instruction if the cpu does not implement the feature. - endmenu # "ARMv8.1 architectural features" menu "ARMv8.2 architectural features" @@ -2109,7 +2094,6 @@ config ARM64_MTE depends on ARM64_AS_HAS_MTE && ARM64_TAGGED_ADDR_ABI depends on AS_HAS_ARMV8_5 # Required for tag checking in the uaccess routines - select ARM64_PAN select ARCH_HAS_SUBPAGE_FAULTS select ARCH_USES_HIGH_VMA_FLAGS select ARCH_USES_PG_ARCH_2 @@ -2141,7 +2125,6 @@ menu "ARMv8.7 architectural features" config ARM64_EPAN bool "Enable support for Enhanced Privileged Access Never (EPAN)" default y - depends on ARM64_PAN help Enhanced Privileged Access Never (EPAN) allows Privileged Access Never to be used with Execute-only mappings. diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index 2c8029472ad4..177c691914f8 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h @@ -19,8 +19,6 @@ cpucap_is_possible(const unsigned int cap) "cap must be < ARM64_NCAPS"); switch (cap) { - case ARM64_HAS_PAN: - return IS_ENABLED(CONFIG_ARM64_PAN); case ARM64_HAS_EPAN: return IS_ENABLED(CONFIG_ARM64_EPAN); case ARM64_SVE: diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 6490930deef8..9810106a3f66 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -124,14 +124,12 @@ static inline bool uaccess_ttbr0_enable(void) static inline void __uaccess_disable_hw_pan(void) { - asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, - CONFIG_ARM64_PAN)); + asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN)); } static inline void __uaccess_enable_hw_pan(void) { - asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, - CONFIG_ARM64_PAN)); + asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN)); } static inline void uaccess_disable_privileged(void) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 547ccf28f289..716440d147a2 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -2164,7 +2164,6 @@ static bool has_bbml2_noabort(const struct arm64_cpu_capabilities *caps, int sco return cpu_supports_bbml2_noabort(); } -#ifdef CONFIG_ARM64_PAN static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused) { /* @@ -2176,7 +2175,6 @@ static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused) sysreg_clear_set(sctlr_el1, SCTLR_EL1_SPAN, 0); set_pstate_pan(1); } -#endif /* CONFIG_ARM64_PAN */ #ifdef CONFIG_ARM64_RAS_EXTN static void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused) @@ -2541,7 +2539,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64MMFR0_EL1, ECV, CNTPOFF) }, -#ifdef CONFIG_ARM64_PAN { .desc = "Privileged Access Never", .capability = ARM64_HAS_PAN, @@ -2550,7 +2547,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .cpu_enable = cpu_enable_pan, ARM64_CPUID_FIELDS(ID_AA64MMFR1_EL1, PAN, IMP) }, -#endif /* CONFIG_ARM64_PAN */ #ifdef CONFIG_ARM64_EPAN { .desc = "Enhanced Privileged Access Never", diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S index 9f4e8d68ab50..11a10d8f5beb 100644 --- a/arch/arm64/kvm/hyp/entry.S +++ b/arch/arm64/kvm/hyp/entry.S @@ -126,7 +126,7 @@ SYM_INNER_LABEL(__guest_exit, SYM_L_GLOBAL) add x1, x1, #VCPU_CONTEXT - ALTERNATIVE(nop, SET_PSTATE_PAN(1), ARM64_HAS_PAN, CONFIG_ARM64_PAN) + ALTERNATIVE(nop, SET_PSTATE_PAN(1), ARM64_HAS_PAN) // Store the guest regs x2 and x3 stp x2, x3, [x1, #CPU_XREG_OFFSET(2)] From 747d5b6735caa0178b3f1c922cfa9c285b093f22 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 15 Jan 2026 09:51:45 +0100 Subject: [PATCH 20/43] arm64: topology: Do not warn on missing AMU in cpuhp_topology_online() When CONFIG_CPUMASK_OFFSTACK is not enabled, and resuming from s2ram on Renesas R-Car H3 (big.LITTLE 4x Cortex-A57 + 4x Cortex-A53), during enabling of the first little core, a warning message is printed: AMU: CPU[4] doesn't support AMU counters This confuses users, as during boot amu_fie_setup() does not print such a message, unless debugging is enabled (freq_counters_valid() prints "CPU%d: counters are not supported.\n" at debug level in that case). Hence drop the warning, freq_counters_valid() has already printed a debug message anyway. Fixes: 6fd9be0b7b2e ("arm64: topology: Handle AMU FIE setup on CPU hotplug") Signed-off-by: Geert Uytterhoeven Reviewed-by: Lifeng Zheng Signed-off-by: Will Deacon --- arch/arm64/kernel/topology.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 539b38935182..3fe1faab0362 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -332,7 +332,6 @@ static int cpuhp_topology_online(unsigned int cpu) * may use different source of the freq scale. */ if (!freq_counters_valid(cpu)) { - pr_warn("CPU[%u] doesn't support AMU counters\n", cpu); topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_ARCH, policy->related_cpus); cpumask_andnot(amu_fie_cpus, amu_fie_cpus, policy->related_cpus); From 53f45f045cbe89024d2b71c145c782bb6a82a602 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 22 Dec 2025 07:28:55 -0800 Subject: [PATCH 21/43] arm64: mm: warn once for ioremap attempts on RAM mappings Replace WARN_ON with WARN_ONCE when detecting attempts to ioremap RAM. This prevents log spam when a misbehaving driver repeatedly tries to map RAM via ioremap. A single warning is more than enough to show the broken code path, and extra reports don't add extra information. Warning floods have been seen in production environments where broken external drivers hit this code path thousand of times, causing unnecessary messages to be printed and pressure on the serial console. Signed-off-by: Breno Leitao Acked-by: Catalin Marinas Signed-off-by: Will Deacon --- arch/arm64/mm/ioremap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c index 10e246f11271..b12cbed9b5ad 100644 --- a/arch/arm64/mm/ioremap.c +++ b/arch/arm64/mm/ioremap.c @@ -24,7 +24,8 @@ void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size, return NULL; /* Don't allow RAM to be mapped. */ - if (WARN_ON(pfn_is_map_memory(__phys_to_pfn(phys_addr)))) + if (WARN_ONCE(pfn_is_map_memory(__phys_to_pfn(phys_addr)), + "ioremap attempted on RAM pfn\n")) return NULL; /* From 2a369c4942489aeab799a7509b7cc721eecafa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 17 Jan 2026 13:10:51 +0100 Subject: [PATCH 22/43] kselftest/arm64: Use syscall() macro over nolibc my_syscall() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The my_syscall*() macros are internal implementation details of nolibc. Nolibc also provides the regular syscall(2), which is also a macro and directly expands to the correct my_syscall(). Use syscall() instead. As a side-effect this fixes some return value checks, as my_syscall() returns the raw value as set by the kernel and does not set errno. Signed-off-by: Thomas Weißschuh Acked-by: Mark Brown Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/abi/tpidr2.c | 3 +- tools/testing/selftests/arm64/gcs/basic-gcs.c | 40 ++++++++----------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/arm64/abi/tpidr2.c b/tools/testing/selftests/arm64/abi/tpidr2.c index 1703543fb7c7..ce4550fb7224 100644 --- a/tools/testing/selftests/arm64/abi/tpidr2.c +++ b/tools/testing/selftests/arm64/abi/tpidr2.c @@ -128,8 +128,7 @@ static int sys_clone(unsigned long clone_flags, unsigned long newsp, int *parent_tidptr, unsigned long tls, int *child_tidptr) { - return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls, - child_tidptr); + return syscall(__NR_clone, clone_flags, newsp, parent_tidptr, tls, child_tidptr); } #define __STACK_SIZE (8 * 1024 * 1024) diff --git a/tools/testing/selftests/arm64/gcs/basic-gcs.c b/tools/testing/selftests/arm64/gcs/basic-gcs.c index 250977abc398..ae4cce6afe2b 100644 --- a/tools/testing/selftests/arm64/gcs/basic-gcs.c +++ b/tools/testing/selftests/arm64/gcs/basic-gcs.c @@ -22,7 +22,7 @@ static size_t page_size = 65536; static __attribute__((noinline)) void valid_gcs_function(void) { /* Do something the compiler can't optimise out */ - my_syscall1(__NR_prctl, PR_SVE_GET_VL); + syscall(__NR_prctl, PR_SVE_GET_VL); } static inline int gcs_set_status(unsigned long mode) @@ -36,12 +36,10 @@ static inline int gcs_set_status(unsigned long mode) * other 3 values passed in registers to the syscall are zero * since the kernel validates them. */ - ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode, - 0, 0, 0); + ret = syscall(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode, 0, 0, 0); if (ret == 0) { - ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, - &new_mode, 0, 0, 0); + ret = syscall(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &new_mode, 0, 0, 0); if (ret == 0) { if (new_mode != mode) { ksft_print_msg("Mode set to %lx not %lx\n", @@ -49,7 +47,7 @@ static inline int gcs_set_status(unsigned long mode) ret = -EINVAL; } } else { - ksft_print_msg("Failed to validate mode: %d\n", ret); + ksft_print_msg("Failed to validate mode: %d\n", errno); } if (enabling != chkfeat_gcs()) { @@ -69,10 +67,9 @@ static bool read_status(void) unsigned long state; int ret; - ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, - &state, 0, 0, 0); + ret = syscall(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &state, 0, 0, 0); if (ret != 0) { - ksft_print_msg("Failed to read state: %d\n", ret); + ksft_print_msg("Failed to read state: %d\n", errno); return false; } @@ -188,9 +185,8 @@ static bool map_guarded_stack(void) int elem; bool pass = true; - buf = (void *)my_syscall3(__NR_map_shadow_stack, 0, page_size, - SHADOW_STACK_SET_MARKER | - SHADOW_STACK_SET_TOKEN); + buf = (void *)syscall(__NR_map_shadow_stack, 0, page_size, + SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN); if (buf == MAP_FAILED) { ksft_print_msg("Failed to map %lu byte GCS: %d\n", page_size, errno); @@ -257,8 +253,7 @@ static bool test_fork(void) valid_gcs_function(); get_gcspr(); - ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, - &child_mode, 0, 0, 0); + ret = syscall(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &child_mode, 0, 0, 0); if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) { ksft_print_msg("GCS not enabled in child\n"); ret = -EINVAL; @@ -321,8 +316,7 @@ static bool test_vfork(void) valid_gcs_function(); get_gcspr(); - ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, - &child_mode, 0, 0, 0); + ret = syscall(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &child_mode, 0, 0, 0); if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) { ksft_print_msg("GCS not enabled in child\n"); ret = EXIT_FAILURE; @@ -390,17 +384,15 @@ int main(void) if (!(getauxval(AT_HWCAP) & HWCAP_GCS)) ksft_exit_skip("SKIP GCS not supported\n"); - ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, - &gcs_mode, 0, 0, 0); + ret = syscall(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode, 0, 0, 0); if (ret != 0) - ksft_exit_fail_msg("Failed to read GCS state: %d\n", ret); + ksft_exit_fail_msg("Failed to read GCS state: %d\n", errno); if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) { gcs_mode = PR_SHADOW_STACK_ENABLE; - ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, - gcs_mode, 0, 0, 0); + ret = syscall(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode, 0, 0, 0); if (ret != 0) - ksft_exit_fail_msg("Failed to enable GCS: %d\n", ret); + ksft_exit_fail_msg("Failed to enable GCS: %d\n", errno); } ksft_set_plan(ARRAY_SIZE(tests)); @@ -410,9 +402,9 @@ int main(void) } /* One last test: disable GCS, we can do this one time */ - ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0); + ret = syscall(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0); if (ret != 0) - ksft_print_msg("Failed to disable GCS: %d\n", ret); + ksft_print_msg("Failed to disable GCS: %d\n", errno); ksft_finished(); From f174a9ffcd48d78a45d560c02ce4071ded036b53 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 19 Jan 2026 10:29:22 +0800 Subject: [PATCH 23/43] KVM: arm64: Add exit to userspace on {LD,ST}64B* outside of memslots The main use of {LD,ST}64B* is to talk to a device, which is hopefully directly assigned to the guest and requires no additional handling. However, this does not preclude a VMM from exposing a virtual device to the guest, and to allow 64 byte accesses as part of the programming interface. A direct consequence of this is that we need to be able to forward such access to userspace. Given that such a contraption is very unlikely to ever exist, we choose to offer a limited service: userspace gets (as part of a new exit reason) the ESR, the IPA, and that's it. It is fully expected to handle the full semantics of the instructions, deal with ACCDATA, the return values and increment PC. Much fun. A canonical implementation can also simply inject an abort and be done with it. Frankly, don't try to do anything else unless you have time to waste. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Marc Zyngier Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- arch/arm64/kvm/mmio.c | 27 ++++++++++++++++++++++++++- include/uapi/linux/kvm.h | 3 ++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/mmio.c b/arch/arm64/kvm/mmio.c index 54f9358c9e0e..e2285ed8c91d 100644 --- a/arch/arm64/kvm/mmio.c +++ b/arch/arm64/kvm/mmio.c @@ -159,6 +159,9 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) bool is_write; int len; u8 data_buf[8]; + u64 esr; + + esr = kvm_vcpu_get_esr(vcpu); /* * No valid syndrome? Ask userspace for help if it has @@ -168,7 +171,7 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) * though, so directly deliver an exception to the guest. */ if (!kvm_vcpu_dabt_isvalid(vcpu)) { - trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu), + trace_kvm_mmio_nisv(*vcpu_pc(vcpu), esr, kvm_vcpu_get_hfar(vcpu), fault_ipa); if (vcpu_is_protected(vcpu)) @@ -185,6 +188,28 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa) return -ENOSYS; } + /* + * When (DFSC == 0b00xxxx || DFSC == 0b10101x) && DFSC != 0b0000xx + * ESR_EL2[12:11] describe the Load/Store Type. This allows us to + * punt the LD64B/ST64B/ST64BV/ST64BV0 instructions to userspace, + * which will have to provide a full emulation of these 4 + * instructions. No, we don't expect this do be fast. + * + * We rely on traps being set if the corresponding features are not + * enabled, so if we get here, userspace has promised us to handle + * it already. + */ + switch (kvm_vcpu_trap_get_fault(vcpu)) { + case 0b000100 ... 0b001111: + case 0b101010 ... 0b101011: + if (FIELD_GET(GENMASK(12, 11), esr)) { + run->exit_reason = KVM_EXIT_ARM_LDST64B; + run->arm_nisv.esr_iss = esr & ~(u64)ESR_ELx_FSC; + run->arm_nisv.fault_ipa = fault_ipa; + return 0; + } + } + /* * Prepare MMIO operation. First decode the syndrome data we get * from the CPU. Then try if some in-kernel emulation feels diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index dddb781b0507..88cca0e22ece 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -180,6 +180,7 @@ struct kvm_xen_exit { #define KVM_EXIT_MEMORY_FAULT 39 #define KVM_EXIT_TDX 40 #define KVM_EXIT_ARM_SEA 41 +#define KVM_EXIT_ARM_LDST64B 42 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -402,7 +403,7 @@ struct kvm_run { } eoi; /* KVM_EXIT_HYPERV */ struct kvm_hyperv_exit hyperv; - /* KVM_EXIT_ARM_NISV */ + /* KVM_EXIT_ARM_NISV / KVM_EXIT_ARM_LDST64B */ struct { __u64 esr_iss; __u64 fault_ipa; From 902eebac8fa3bad1c369f48f2eaf859755ad9e6d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 19 Jan 2026 10:29:23 +0800 Subject: [PATCH 24/43] KVM: arm64: Add documentation for KVM_EXIT_ARM_LDST64B Add a bit of documentation for KVM_EXIT_ARM_LDST64B so that userspace knows what to expect. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Marc Zyngier Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- Documentation/virt/kvm/api.rst | 43 ++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 01a3abef8abb..bfa0ab343081 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -1303,12 +1303,13 @@ userspace, for example because of missing instruction syndrome decode information or because there is no device mapped at the accessed IPA, then userspace can ask the kernel to inject an external abort using the address from the exiting fault on the VCPU. It is a programming error to set -ext_dabt_pending after an exit which was not either KVM_EXIT_MMIO or -KVM_EXIT_ARM_NISV. This feature is only available if the system supports -KVM_CAP_ARM_INJECT_EXT_DABT. This is a helper which provides commonality in -how userspace reports accesses for the above cases to guests, across different -userspace implementations. Nevertheless, userspace can still emulate all Arm -exceptions by manipulating individual registers using the KVM_SET_ONE_REG API. +ext_dabt_pending after an exit which was not either KVM_EXIT_MMIO, +KVM_EXIT_ARM_NISV, or KVM_EXIT_ARM_LDST64B. This feature is only available if +the system supports KVM_CAP_ARM_INJECT_EXT_DABT. This is a helper which +provides commonality in how userspace reports accesses for the above cases to +guests, across different userspace implementations. Nevertheless, userspace +can still emulate all Arm exceptions by manipulating individual registers +using the KVM_SET_ONE_REG API. See KVM_GET_VCPU_EVENTS for the data structure. @@ -7050,12 +7051,14 @@ in send_page or recv a buffer to recv_page). :: - /* KVM_EXIT_ARM_NISV */ + /* KVM_EXIT_ARM_NISV / KVM_EXIT_ARM_LDST64B */ struct { __u64 esr_iss; __u64 fault_ipa; } arm_nisv; +- KVM_EXIT_ARM_NISV: + Used on arm64 systems. If a guest accesses memory not in a memslot, KVM will typically return to userspace and ask it to do MMIO emulation on its behalf. However, for certain classes of instructions, no instruction decode @@ -7089,6 +7092,32 @@ Note that although KVM_CAP_ARM_NISV_TO_USER will be reported if queried outside of a protected VM context, the feature will not be exposed if queried on a protected VM file descriptor. +- KVM_EXIT_ARM_LDST64B: + +Used on arm64 systems. When a guest using a LD64B, ST64B, ST64BV, ST64BV0, +outside of a memslot, KVM will return to userspace with KVM_EXIT_ARM_LDST64B, +exposing the relevant ESR_EL2 information and faulting IPA, similarly to +KVM_EXIT_ARM_NISV. + +Userspace is supposed to fully emulate the instructions, which includes: + + - fetch of the operands for a store, including ACCDATA_EL1 in the case + of a ST64BV0 instruction + - deal with the endianness if the guest is big-endian + - emulate the access, including the delivery of an exception if the + access didn't succeed + - provide a return value in the case of ST64BV/ST64BV0 + - return the data in the case of a load + - increment PC if the instruction was successfully executed + +Note that there is no expectation of performance for this emulation, as it +involves a large number of interaction with the guest state. It is, however, +expected that the instruction's semantics are preserved, specially the +single-copy atomicity property of the 64 byte access. + +This exit reason must be handled if userspace sets ID_AA64ISAR1_EL1.LS64 to a +non-zero value, indicating that FEAT_LS64* is enabled. + :: /* KVM_EXIT_X86_RDMSR / KVM_EXIT_X86_WRMSR */ From 2937aeec9dc5d25a02c1415a56d88ee4cc17ad83 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 19 Jan 2026 10:29:24 +0800 Subject: [PATCH 25/43] KVM: arm64: Handle DABT caused by LS64* instructions on unsupported memory If FEAT_LS64WB not supported, FEAT_LS64* instructions only support to access Device/Uncacheable memory, otherwise a data abort for unsupported Exclusive or atomic access (0x35, UAoEF) is generated per spec. It's implementation defined whether the target exception level is routed and is possible to implemented as route to EL2 on a VHE VM according to DDI0487L.b Section C3.2.6 Single-copy atomic 64-byte load/store. If it's implemented as generate the DABT to the final enabled stage (stage-2), inject the UAoEF back to the guest after checking the memslot is valid. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- arch/arm64/include/asm/esr.h | 8 +++++++ arch/arm64/include/asm/kvm_emulate.h | 1 + arch/arm64/kvm/inject_fault.c | 34 ++++++++++++++++++++++++++++ arch/arm64/kvm/mmu.c | 14 +++++++++++- 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 4975a92cbd17..7e86d400864e 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -124,6 +124,7 @@ #define ESR_ELx_FSC_SEA_TTW(n) (0x14 + (n)) #define ESR_ELx_FSC_SECC (0x18) #define ESR_ELx_FSC_SECC_TTW(n) (0x1c + (n)) +#define ESR_ELx_FSC_EXCL_ATOMIC (0x35) #define ESR_ELx_FSC_ADDRSZ (0x00) /* @@ -488,6 +489,13 @@ static inline bool esr_fsc_is_access_flag_fault(unsigned long esr) (esr == ESR_ELx_FSC_ACCESS_L(0)); } +static inline bool esr_fsc_is_excl_atomic_fault(unsigned long esr) +{ + esr = esr & ESR_ELx_FSC; + + return esr == ESR_ELx_FSC_EXCL_ATOMIC; +} + static inline bool esr_fsc_is_addr_sz_fault(unsigned long esr) { esr &= ESR_ELx_FSC; diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index c9eab316398e..bab967d65715 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -47,6 +47,7 @@ void kvm_skip_instr32(struct kvm_vcpu *vcpu); void kvm_inject_undefined(struct kvm_vcpu *vcpu); int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr); int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr); +int kvm_inject_dabt_excl_atomic(struct kvm_vcpu *vcpu, u64 addr); void kvm_inject_size_fault(struct kvm_vcpu *vcpu); static inline int kvm_inject_sea_dabt(struct kvm_vcpu *vcpu, u64 addr) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index dfcd66c65517..6cc7ad84d7d8 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -253,6 +253,40 @@ int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr) return 1; } +static int kvm_inject_nested_excl_atomic(struct kvm_vcpu *vcpu, u64 addr) +{ + u64 esr = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_DABT_LOW) | + FIELD_PREP(ESR_ELx_FSC, ESR_ELx_FSC_EXCL_ATOMIC) | + ESR_ELx_IL; + + vcpu_write_sys_reg(vcpu, addr, FAR_EL2); + return kvm_inject_nested_sync(vcpu, esr); +} + +/** + * kvm_inject_dabt_excl_atomic - inject a data abort for unsupported exclusive + * or atomic access + * @vcpu: The VCPU to receive the data abort + * @addr: The address to report in the DFAR + * + * It is assumed that this code is called from the VCPU thread and that the + * VCPU therefore is not currently executing guest code. + */ +int kvm_inject_dabt_excl_atomic(struct kvm_vcpu *vcpu, u64 addr) +{ + u64 esr; + + if (is_nested_ctxt(vcpu) && (vcpu_read_sys_reg(vcpu, HCR_EL2) & HCR_VM)) + return kvm_inject_nested_excl_atomic(vcpu, addr); + + __kvm_inject_sea(vcpu, false, addr); + esr = vcpu_read_sys_reg(vcpu, exception_esr_elx(vcpu)); + esr &= ~ESR_ELx_FSC; + esr |= ESR_ELx_FSC_EXCL_ATOMIC; + vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu)); + return 1; +} + void kvm_inject_size_fault(struct kvm_vcpu *vcpu) { unsigned long addr, esr; diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 48d7c372a4cd..edc348431d71 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1845,6 +1845,17 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, return ret; } + /* + * Guest performs atomic/exclusive operations on memory with unsupported + * attributes (e.g. ld64b/st64b on normal memory when no FEAT_LS64WB) + * and trigger the exception here. Since the memslot is valid, inject + * the fault back to the guest. + */ + if (esr_fsc_is_excl_atomic_fault(kvm_vcpu_get_esr(vcpu))) { + kvm_inject_dabt_excl_atomic(vcpu, kvm_vcpu_get_hfar(vcpu)); + return 1; + } + if (nested) adjust_nested_fault_perms(nested, &prot, &writable); @@ -2082,7 +2093,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) /* Check the stage-2 fault is trans. fault or write fault */ if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr) && - !esr_fsc_is_access_flag_fault(esr)) { + !esr_fsc_is_access_flag_fault(esr) && + !esr_fsc_is_excl_atomic_fault(esr)) { kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n", kvm_vcpu_trap_get_class(vcpu), (unsigned long)kvm_vcpu_trap_get_fault(vcpu), From dea58da4b6fede082d9f38ce069090fd6d43f4e2 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 19 Jan 2026 10:29:25 +0800 Subject: [PATCH 26/43] arm64: Provide basic EL2 setup for FEAT_{LS64, LS64_V} usage at EL0/1 Instructions introduced by FEAT_{LS64, LS64_V} is controlled by HCRX_EL2.{EnALS, EnASR}. Configure all of these to allow usage at EL0/1. This doesn't mean these instructions are always available in EL0/1 if provided. The hypervisor still have the control at runtime. Acked-by: Will Deacon Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- arch/arm64/include/asm/el2_setup.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index cacd20df1786..9393220de069 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -83,9 +83,19 @@ /* Enable GCS if supported */ mrs_s x1, SYS_ID_AA64PFR1_EL1 ubfx x1, x1, #ID_AA64PFR1_EL1_GCS_SHIFT, #4 - cbz x1, .Lset_hcrx_\@ + cbz x1, .Lskip_gcs_hcrx_\@ orr x0, x0, #HCRX_EL2_GCSEn +.Lskip_gcs_hcrx_\@: + /* Enable LS64, LS64_V if supported */ + mrs_s x1, SYS_ID_AA64ISAR1_EL1 + ubfx x1, x1, #ID_AA64ISAR1_EL1_LS64_SHIFT, #4 + cbz x1, .Lset_hcrx_\@ + orr x0, x0, #HCRX_EL2_EnALS + cmp x1, #ID_AA64ISAR1_EL1_LS64_LS64_V + b.lt .Lset_hcrx_\@ + orr x0, x0, #HCRX_EL2_EnASR + .Lset_hcrx_\@: msr_s SYS_HCRX_EL2, x0 .Lskip_hcrx_\@: From 151b92c92a45704216c37d6238efbffd84aac538 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 19 Jan 2026 10:29:26 +0800 Subject: [PATCH 27/43] KVM: arm64: Enable FEAT_{LS64, LS64_V} in the supported guest Using FEAT_{LS64, LS64_V} instructions in a guest is also controlled by HCRX_EL2.{EnALS, EnASR}. Enable it if guest has related feature. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- arch/arm64/include/asm/kvm_emulate.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index bab967d65715..29291e25ecfd 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -695,6 +695,12 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu) if (kvm_has_sctlr2(kvm)) vcpu->arch.hcrx_el2 |= HCRX_EL2_SCTLR2En; + + if (kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64)) + vcpu->arch.hcrx_el2 |= HCRX_EL2_EnALS; + + if (kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64_V)) + vcpu->arch.hcrx_el2 |= HCRX_EL2_EnASR; } } #endif /* __ARM64_KVM_EMULATE_H__ */ From 58ce78667a641f93afa0c152c700a1673383d323 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 19 Jan 2026 10:29:27 +0800 Subject: [PATCH 28/43] arm64: Add support for FEAT_{LS64, LS64_V} Armv8.7 introduces single-copy atomic 64-byte loads and stores instructions and its variants named under FEAT_{LS64, LS64_V}. These features are identified by ID_AA64ISAR1_EL1.LS64 and the use of such instructions in userspace (EL0) can be trapped. As st64bv (FEAT_LS64_V) and st64bv0 (FEAT_LS64_ACCDATA) can not be tell apart, FEAT_LS64 and FEAT_LS64_ACCDATA which will be supported in later patch will be exported to userspace, FEAT_LS64_V will be enabled only in kernel. In order to support the use of corresponding instructions in userspace: - Make ID_AA64ISAR1_EL1.LS64 visbile to userspace - Add identifying and enabling in the cpufeature list - Expose these support of these features to userspace through HWCAP3 and cpuinfo ld64b/st64b (FEAT_LS64) and st64bv (FEAT_LS64_V) is intended for special memory (device memory) so requires support by the CPU, system and target memory location (device that support these instructions). The HWCAP3_LS64, implies the support of CPU and system (since no identification method from system, so SoC vendors should advertise support in the CPU if system also support them). Otherwise for ld64b/st64b the atomicity may not be guaranteed or a DABT will be generated, so users (probably userspace driver developer) should make sure the target memory (device) also have the support. For st64bv 0xffffffffffffffff will be returned as status result for unsupported memory so user should check it. Document the restrictions along with HWCAP3_LS64. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- Documentation/arch/arm64/booting.rst | 12 +++++++++++ Documentation/arch/arm64/elf_hwcaps.rst | 7 +++++++ arch/arm64/include/asm/hwcap.h | 1 + arch/arm64/include/uapi/asm/hwcap.h | 1 + arch/arm64/kernel/cpufeature.c | 28 +++++++++++++++++++++++++ arch/arm64/kernel/cpuinfo.c | 1 + arch/arm64/tools/cpucaps | 2 ++ 7 files changed, 52 insertions(+) diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst index 26efca09aef3..13ef311dace8 100644 --- a/Documentation/arch/arm64/booting.rst +++ b/Documentation/arch/arm64/booting.rst @@ -556,6 +556,18 @@ Before jumping into the kernel, the following conditions must be met: - MDCR_EL3.TPM (bit 6) must be initialized to 0b0 + For CPUs with support for 64-byte loads and stores without status (FEAT_LS64): + + - If the kernel is entered at EL1 and EL2 is present: + + - HCRX_EL2.EnALS (bit 1) must be initialised to 0b1. + + For CPUs with support for 64-byte stores with status (FEAT_LS64_V): + + - If the kernel is entered at EL1 and EL2 is present: + + - HCRX_EL2.EnASR (bit 2) must be initialised to 0b1. + The requirements described above for CPU mode, caches, MMUs, architected timers, coherency and system registers apply to all CPUs. All CPUs must enter the kernel in the same exception level. Where the values documented diff --git a/Documentation/arch/arm64/elf_hwcaps.rst b/Documentation/arch/arm64/elf_hwcaps.rst index a15df4956849..97315ae6c0da 100644 --- a/Documentation/arch/arm64/elf_hwcaps.rst +++ b/Documentation/arch/arm64/elf_hwcaps.rst @@ -444,6 +444,13 @@ HWCAP3_MTE_STORE_ONLY HWCAP3_LSFE Functionality implied by ID_AA64ISAR3_EL1.LSFE == 0b0001 +HWCAP3_LS64 + Functionality implied by ID_AA64ISAR1_EL1.LS64 == 0b0001. Note that + the function of instruction ld64b/st64b requires support by CPU, system + and target (device) memory location and HWCAP3_LS64 implies the support + of CPU. User should only use ld64b/st64b on supported target (device) + memory location, otherwise fallback to the non-atomic alternatives. + 4. Unused AT_HWCAP bits ----------------------- diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index 1f63814ae6c4..72ea4bda79f3 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -179,6 +179,7 @@ #define KERNEL_HWCAP_MTE_FAR __khwcap3_feature(MTE_FAR) #define KERNEL_HWCAP_MTE_STORE_ONLY __khwcap3_feature(MTE_STORE_ONLY) #define KERNEL_HWCAP_LSFE __khwcap3_feature(LSFE) +#define KERNEL_HWCAP_LS64 __khwcap3_feature(LS64) /* * This yields a mask that user programs can use to figure out what diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index 575564ecdb0b..06f83ca8de56 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -146,5 +146,6 @@ #define HWCAP3_MTE_FAR (1UL << 0) #define HWCAP3_MTE_STORE_ONLY (1UL << 1) #define HWCAP3_LSFE (1UL << 2) +#define HWCAP3_LS64 (1UL << 3) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 716440d147a2..c688d2225c94 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -240,6 +240,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar0[] = { }; static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_EL1_LS64_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_EL1_XS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_EL1_I8MM_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR1_EL1_DGH_SHIFT, 4, 0), @@ -2258,6 +2259,16 @@ static void cpu_enable_e0pd(struct arm64_cpu_capabilities const *cap) } #endif /* CONFIG_ARM64_E0PD */ +static void cpu_enable_ls64(struct arm64_cpu_capabilities const *cap) +{ + sysreg_clear_set(sctlr_el1, SCTLR_EL1_EnALS, SCTLR_EL1_EnALS); +} + +static void cpu_enable_ls64_v(struct arm64_cpu_capabilities const *cap) +{ + sysreg_clear_set(sctlr_el1, SCTLR_EL1_EnASR, 0); +} + #ifdef CONFIG_ARM64_PSEUDO_NMI static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry, int scope) @@ -3142,6 +3153,22 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64MMFR1_EL1, XNX, IMP) }, + { + .desc = "LS64", + .capability = ARM64_HAS_LS64, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + .cpu_enable = cpu_enable_ls64, + ARM64_CPUID_FIELDS(ID_AA64ISAR1_EL1, LS64, LS64) + }, + { + .desc = "LS64_V", + .capability = ARM64_HAS_LS64_V, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + .cpu_enable = cpu_enable_ls64_v, + ARM64_CPUID_FIELDS(ID_AA64ISAR1_EL1, LS64, LS64_V) + }, {}, }; @@ -3261,6 +3288,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(ID_AA64ISAR1_EL1, BF16, EBF16, CAP_HWCAP, KERNEL_HWCAP_EBF16), HWCAP_CAP(ID_AA64ISAR1_EL1, DGH, IMP, CAP_HWCAP, KERNEL_HWCAP_DGH), HWCAP_CAP(ID_AA64ISAR1_EL1, I8MM, IMP, CAP_HWCAP, KERNEL_HWCAP_I8MM), + HWCAP_CAP(ID_AA64ISAR1_EL1, LS64, LS64, CAP_HWCAP, KERNEL_HWCAP_LS64), HWCAP_CAP(ID_AA64ISAR2_EL1, LUT, IMP, CAP_HWCAP, KERNEL_HWCAP_LUT), HWCAP_CAP(ID_AA64ISAR3_EL1, FAMINMAX, IMP, CAP_HWCAP, KERNEL_HWCAP_FAMINMAX), HWCAP_CAP(ID_AA64ISAR3_EL1, LSFE, IMP, CAP_HWCAP, KERNEL_HWCAP_LSFE), diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index c44e6d94f5de..6149bc91251d 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -81,6 +81,7 @@ static const char *const hwcap_str[] = { [KERNEL_HWCAP_PACA] = "paca", [KERNEL_HWCAP_PACG] = "pacg", [KERNEL_HWCAP_GCS] = "gcs", + [KERNEL_HWCAP_LS64] = "ls64", [KERNEL_HWCAP_DCPODP] = "dcpodp", [KERNEL_HWCAP_SVE2] = "sve2", [KERNEL_HWCAP_SVEAES] = "sveaes", diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 0fac75f01534..50a0c695f340 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -46,6 +46,8 @@ HAS_HCX HAS_LDAPR HAS_LPA2 HAS_LSE_ATOMICS +HAS_LS64 +HAS_LS64_V HAS_MOPS HAS_NESTED_VIRT HAS_BBML2_NOABORT From 57a96356bb6942e16283138d0a42baad29169ed8 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 19 Jan 2026 10:29:28 +0800 Subject: [PATCH 29/43] kselftest/arm64: Add HWCAP test for FEAT_LS64 Add tests for FEAT_LS64. Issue related instructions if feature presents, no SIGILL should be received. When such instructions operate on Device memory or non-cacheable memory, we may received a SIGBUS during the test (w/o FEAT_LS64WB). Just ignore it since we only tested whether the instruction itself can be issued as expected on platforms declaring the support of such features. Acked-by: Arnd Bergmann Acked-by: Oliver Upton Signed-off-by: Yicong Yang Signed-off-by: Zhou Wang Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/abi/hwcap.c | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tools/testing/selftests/arm64/abi/hwcap.c b/tools/testing/selftests/arm64/abi/hwcap.c index c41640f18e4e..9d2df1f3e6bb 100644 --- a/tools/testing/selftests/arm64/abi/hwcap.c +++ b/tools/testing/selftests/arm64/abi/hwcap.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -595,6 +597,45 @@ static void lrcpc3_sigill(void) : "=r" (data0), "=r" (data1) : "r" (src) :); } +static void ignore_signal(int sig, siginfo_t *info, void *context) +{ + ucontext_t *uc = context; + + uc->uc_mcontext.pc += 4; +} + +static void ls64_sigill(void) +{ + struct sigaction ign, old; + char src[64] __aligned(64) = { 1 }; + + /* + * LS64 requires target memory to be Device/Non-cacheable (if + * FEAT_LS64WB not supported) and the completer supports these + * instructions, otherwise we'll receive a SIGBUS. Since we are only + * testing the ABI here, so just ignore the SIGBUS and see if we can + * execute the instructions without receiving a SIGILL. Restore the + * handler of SIGBUS after this test. + */ + ign.sa_sigaction = ignore_signal; + ign.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&ign.sa_mask); + sigaction(SIGBUS, &ign, &old); + + register void *xn asm ("x8") = src; + register u64 xt_1 asm ("x0"); + + /* LD64B x0, [x8] */ + asm volatile(".inst 0xf83fd100" : "=r" (xt_1) : "r" (xn) + : "x1", "x2", "x3", "x4", "x5", "x6", "x7"); + + /* ST64B x0, [x8] */ + asm volatile(".inst 0xf83f9100" : : "r" (xt_1), "r" (xn) + : "x1", "x2", "x3", "x4", "x5", "x6", "x7"); + + sigaction(SIGBUS, &old, NULL); +} + static const struct hwcap_data { const char *name; unsigned long at_hwcap; @@ -1134,6 +1175,14 @@ static const struct hwcap_data { .hwcap_bit = HWCAP3_MTE_STORE_ONLY, .cpuinfo = "mtestoreonly", }, + { + .name = "LS64", + .at_hwcap = AT_HWCAP3, + .hwcap_bit = HWCAP3_LS64, + .cpuinfo = "ls64", + .sigill_fn = ls64_sigill, + .sigill_reliable = true, + }, }; typedef void (*sighandler_fn)(int, siginfo_t *, void *); From 3fed7e0059f0af1d0e71e165145a1e3030526835 Mon Sep 17 00:00:00 2001 From: Lucas Wei Date: Wed, 14 Jan 2026 14:52:41 +0000 Subject: [PATCH 30/43] arm64: errata: Workaround for SI L1 downstream coherency issue When software issues a Cache Maintenance Operation (CMO) targeting a dirty cache line, the CPU and DSU cluster may optimize the operation by combining the CopyBack Write and CMO into a single combined CopyBack Write plus CMO transaction presented to the interconnect (MCN). For these combined transactions, the MCN splits the operation into two separate transactions, one Write and one CMO, and then propagates the write and optionally the CMO to the downstream memory system or external Point of Serialization (PoS). However, the MCN may return an early CompCMO response to the DSU cluster before the corresponding Write and CMO transactions have completed at the external PoS or downstream memory. As a result, stale data may be observed by external observers that are directly connected to the external PoS or downstream memory. This erratum affects any system topology in which the following conditions apply: - The Point of Serialization (PoS) is located downstream of the interconnect. - A downstream observer accesses memory directly, bypassing the interconnect. Conditions: This erratum occurs only when all of the following conditions are met: 1. Software executes a data cache maintenance operation, specifically, a clean or clean&invalidate by virtual address (DC CVAC or DC CIVAC), that hits on unique dirty data in the CPU or DSU cache. This results in a combined CopyBack and CMO being issued to the interconnect. 2. The interconnect splits the combined transaction into separate Write and CMO transactions and returns an early completion response to the CPU or DSU before the write has completed at the downstream memory or PoS. 3. A downstream observer accesses the affected memory address after the early completion response is issued but before the actual memory write has completed. This allows the observer to read stale data that has not yet been updated at the PoS or downstream memory. The implementation of workaround put a second loop of CMOs at the same virtual address whose operation meet erratum conditions to wait until cache data be cleaned to PoC. This way of implementation mitigates performance penalty compared to purely duplicate original CMO. Signed-off-by: Lucas Wei Signed-off-by: Will Deacon --- Documentation/arch/arm64/silicon-errata.rst | 1 + arch/arm64/Kconfig | 19 +++++++++++++ arch/arm64/include/asm/assembler.h | 10 +++++++ arch/arm64/kernel/cpu_errata.c | 31 +++++++++++++++++++++ arch/arm64/tools/cpucaps | 1 + 5 files changed, 62 insertions(+) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index a7ec57060f64..4c300caad901 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -212,6 +212,7 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | GIC-700 | #2941627 | ARM64_ERRATUM_2941627 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | SI L1 | #4311569 | ARM64_ERRATUM_4311569 | +----------------+-----------------+-----------------+-----------------------------+ | Broadcom | Brahma-B53 | N/A | ARM64_ERRATUM_845719 | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 93173f0a09c7..89326bb26f48 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1155,6 +1155,25 @@ config ARM64_ERRATUM_3194386 If unsure, say Y. +config ARM64_ERRATUM_4311569 + bool "SI L1: 4311569: workaround for premature CMO completion erratum" + default y + help + This option adds the workaround for ARM SI L1 erratum 4311569. + + The erratum of SI L1 can cause an early response to a combined write + and cache maintenance operation (WR+CMO) before the operation is fully + completed to the Point of Serialization (POS). + This can result in a non-I/O coherent agent observing stale data, + potentially leading to system instability or incorrect behavior. + + Enabling this option implements a software workaround by inserting a + second loop of Cache Maintenance Operation (CMO) immediately following the + end of function to do CMOs. This ensures that the data is correctly serialized + before the buffer is handed off to a non-coherent agent. + + If unsure, say Y. + config CAVIUM_ERRATUM_22375 bool "Cavium erratum 22375, 24313" default y diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index f0ca7196f6fa..d3d46e5f7188 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -381,6 +381,9 @@ alternative_endif .macro dcache_by_myline_op op, domain, start, end, linesz, tmp, fixup sub \tmp, \linesz, #1 bic \start, \start, \tmp +alternative_if ARM64_WORKAROUND_4311569 + mov \tmp, \start +alternative_else_nop_endif .Ldcache_op\@: .ifc \op, cvau __dcache_op_workaround_clean_cache \op, \start @@ -402,6 +405,13 @@ alternative_endif add \start, \start, \linesz cmp \start, \end b.lo .Ldcache_op\@ +alternative_if ARM64_WORKAROUND_4311569 + .ifnc \op, cvau + mov \start, \tmp + mov \tmp, xzr + cbnz \start, .Ldcache_op\@ + .endif +alternative_else_nop_endif dsb \domain _cond_uaccess_extable .Ldcache_op\@, \fixup diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 8cb3b575a031..5c0ab6bfd44a 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -141,6 +141,30 @@ has_mismatched_cache_type(const struct arm64_cpu_capabilities *entry, return (ctr_real != sys) && (ctr_raw != sys); } +#ifdef CONFIG_ARM64_ERRATUM_4311569 +static DEFINE_STATIC_KEY_FALSE(arm_si_l1_workaround_4311569); +static int __init early_arm_si_l1_workaround_4311569_cfg(char *arg) +{ + static_branch_enable(&arm_si_l1_workaround_4311569); + pr_info("Enabling cache maintenance workaround for ARM SI-L1 erratum 4311569\n"); + + return 0; +} +early_param("arm_si_l1_workaround_4311569", early_arm_si_l1_workaround_4311569_cfg); + +/* + * We have some earlier use cases to call cache maintenance operation functions, for example, + * dcache_inval_poc() and dcache_clean_poc() in head.S, before making decision to turn on this + * workaround. Since the scope of this workaround is limited to non-coherent DMA agents, its + * safe to have the workaround off by default. + */ +static bool +need_arm_si_l1_workaround_4311569(const struct arm64_cpu_capabilities *entry, int scope) +{ + return static_branch_unlikely(&arm_si_l1_workaround_4311569); +} +#endif + static void cpu_enable_trap_ctr_access(const struct arm64_cpu_capabilities *cap) { @@ -870,6 +894,13 @@ const struct arm64_cpu_capabilities arm64_errata[] = { ERRATA_MIDR_RANGE_LIST(erratum_spec_ssbs_list), }, #endif +#ifdef CONFIG_ARM64_ERRATUM_4311569 + { + .capability = ARM64_WORKAROUND_4311569, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = need_arm_si_l1_workaround_4311569, + }, +#endif #ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD { .desc = "ARM errata 2966298, 3117295", diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 0fac75f01534..856b6cf6e71e 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -103,6 +103,7 @@ WORKAROUND_2077057 WORKAROUND_2457168 WORKAROUND_2645198 WORKAROUND_2658417 +WORKAROUND_4311569 WORKAROUND_AMPERE_AC03_CPU_38 WORKAROUND_AMPERE_AC04_CPU_23 WORKAROUND_TRBE_OVERWRITE_FILL_MODE From 3c58f03e805f8f9025f09fe393103947dca49c57 Mon Sep 17 00:00:00 2001 From: I-Hsin Cheng Date: Sat, 24 Jan 2026 20:32:41 +0800 Subject: [PATCH 31/43] kselftest/arm64: Add missing file in .gitignore The binary generated by check_hugetlb_options is missing in .gitignore under the directory. Add it into the file so it won't be logged into version control. Signed-off-by: I-Hsin Cheng Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/mte/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/arm64/mte/.gitignore b/tools/testing/selftests/arm64/mte/.gitignore index 052d0f9f92b3..f6937f890039 100644 --- a/tools/testing/selftests/arm64/mte/.gitignore +++ b/tools/testing/selftests/arm64/mte/.gitignore @@ -6,3 +6,4 @@ check_mmap_options check_prctl check_ksm_options check_user_mem +check_hugetlb_options From e7e7afdc7c141227f2ce29aca85969e050da1ab9 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 22 Dec 2025 19:47:22 +0800 Subject: [PATCH 32/43] arm64: Remove unused _TIF_WORK_MASK Since commit b3cf07851b6c ("arm64: entry: Switch to generic IRQ entry"), _TIF_WORK_MASK is never used, so remove it. Reviewed-by: Anshuman Khandual Reviewed-by: Kevin Brodsky Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon --- arch/arm64/include/asm/thread_info.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index a803b887b0b4..24fcd6adaa33 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -106,12 +106,6 @@ void arch_setup_new_exec(void); #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) #define _TIF_TSC_SIGSEGV (1 << TIF_TSC_SIGSEGV) -#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY | \ - _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ - _TIF_UPROBE | _TIF_MTE_ASYNC_FAULT | \ - _TIF_NOTIFY_SIGNAL | _TIF_SIGPENDING | \ - _TIF_PATCH_PENDING) - #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \ _TIF_SYSCALL_EMU) From 741a9000173a036526eb2374e1ba34c466754f1c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 22 Dec 2025 19:47:23 +0800 Subject: [PATCH 33/43] arm64/ptrace: Split report_syscall() The generic syscall entry code has the form: | syscall_trace_enter() | { | ptrace_report_syscall_entry() | } | | syscall_exit_work() | { | ptrace_report_syscall_exit() | } In preparation for moving arm64 over to the generic entry code, split report_syscall() to two separate enter and exit functions to align the structure of the arm64 code with syscall_trace_enter() and syscall_exit_work() from the generic entry code. No functional changes. Reviewed-by: Anshuman Khandual Reviewed-by: Kevin Brodsky Suggested-by: Mark Rutland Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon --- arch/arm64/kernel/ptrace.c | 41 +++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index b9bdd83fbbca..03fe2f8a4d54 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2343,9 +2343,10 @@ enum ptrace_syscall_dir { PTRACE_SYSCALL_EXIT, }; -static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir) +static __always_inline unsigned long ptrace_save_reg(struct pt_regs *regs, + enum ptrace_syscall_dir dir, + int *regno) { - int regno; unsigned long saved_reg; /* @@ -2364,15 +2365,31 @@ static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir) * - Syscall stops behave differently to seccomp and pseudo-step traps * (the latter do not nobble any registers). */ - regno = (is_compat_task() ? 12 : 7); - saved_reg = regs->regs[regno]; - regs->regs[regno] = dir; + *regno = (is_compat_task() ? 12 : 7); + saved_reg = regs->regs[*regno]; + regs->regs[*regno] = dir; - if (dir == PTRACE_SYSCALL_ENTER) { - if (ptrace_report_syscall_entry(regs)) - forget_syscall(regs); - regs->regs[regno] = saved_reg; - } else if (!test_thread_flag(TIF_SINGLESTEP)) { + return saved_reg; +} + +static void report_syscall_entry(struct pt_regs *regs) +{ + unsigned long saved_reg; + int regno; + + saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, ®no); + if (ptrace_report_syscall_entry(regs)) + forget_syscall(regs); + regs->regs[regno] = saved_reg; +} + +static void report_syscall_exit(struct pt_regs *regs) +{ + unsigned long saved_reg; + int regno; + + saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_EXIT, ®no); + if (!test_thread_flag(TIF_SINGLESTEP)) { ptrace_report_syscall_exit(regs, 0); regs->regs[regno] = saved_reg; } else { @@ -2392,7 +2409,7 @@ int syscall_trace_enter(struct pt_regs *regs) unsigned long flags = read_thread_flags(); if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) { - report_syscall(regs, PTRACE_SYSCALL_ENTER); + report_syscall_entry(regs); if (flags & _TIF_SYSCALL_EMU) return NO_SYSCALL; } @@ -2420,7 +2437,7 @@ void syscall_trace_exit(struct pt_regs *regs) trace_sys_exit(regs, syscall_get_return_value(current, regs)); if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP)) - report_syscall(regs, PTRACE_SYSCALL_EXIT); + report_syscall_exit(regs); rseq_syscall(regs); } From a3386301667ed03ba9baeb6a2629e726714cc9a7 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 22 Dec 2025 19:47:24 +0800 Subject: [PATCH 34/43] arm64/ptrace: Return early for ptrace_report_syscall_entry() error The generic entry abort the syscall_trace_enter() sequence if ptrace_report_syscall_entry() errors out, but arm64 not. When ptrace requests interception, it should prevent all subsequent system-call processing, including audit and seccomp. In preparation for moving arm64 over to the generic entry code, return early if ptrace_report_syscall_entry() encounters an error. Reviewed-by: Kevin Brodsky Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon --- arch/arm64/kernel/ptrace.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 03fe2f8a4d54..f333791ffba6 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2372,15 +2372,18 @@ static __always_inline unsigned long ptrace_save_reg(struct pt_regs *regs, return saved_reg; } -static void report_syscall_entry(struct pt_regs *regs) +static int report_syscall_entry(struct pt_regs *regs) { unsigned long saved_reg; - int regno; + int regno, ret; saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, ®no); - if (ptrace_report_syscall_entry(regs)) + ret = ptrace_report_syscall_entry(regs); + if (ret) forget_syscall(regs); regs->regs[regno] = saved_reg; + + return ret; } static void report_syscall_exit(struct pt_regs *regs) @@ -2407,10 +2410,11 @@ static void report_syscall_exit(struct pt_regs *regs) int syscall_trace_enter(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); + int ret; if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) { - report_syscall_entry(regs); - if (flags & _TIF_SYSCALL_EMU) + ret = report_syscall_entry(regs); + if (ret || (flags & _TIF_SYSCALL_EMU)) return NO_SYSCALL; } From a4e5927115f30a301f9939ed43e6a21a343e06ad Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 15 Jan 2026 15:07:18 -0800 Subject: [PATCH 35/43] arm64: mte: Set TCMA1 whenever MTE is present in the kernel Set the TCMA1 bit so that access to TTBR1 addresses with 0xf in their tag bits will be treated as tag unchecked. This is important to avoid unwanted tag checking on some systems. Specifically, SCTLR_EL1.TCF can be set to indicate that no tag check faults are desired. But the architecture doesn't guarantee that in this case the system won't still perform tag checks. Use TCMA1 to ensure that undesired tag checks are not performed. This bit was already set in the KASAN case. Adding it to the non-KASAN case prevents tag checking since all TTBR1 address will have a value of 0xf in their tag bits. This patch has been measured on an Ampere system to improve the following: * Eliminate over 98% of kernel-side tag checks during "perf bench futex hash", as measured with "perf stat". * Eliminate all MTE overhead (was previously a 25% performance penalty) from the Phoronix pts/memcached benchmark (1:10 Set:Get ration with 96 cores). Reported-by: Taehyun Noh Suggested-by: Catalin Marinas Signed-off-by: Carl Worth Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon --- arch/arm64/mm/proc.S | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 01e868116448..680ff21d79f1 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -48,14 +48,14 @@ #define TCR_KASAN_SW_FLAGS 0 #endif -#ifdef CONFIG_KASAN_HW_TAGS -#define TCR_MTE_FLAGS TCR_EL1_TCMA1 | TCR_EL1_TBI1 | TCR_EL1_TBID1 -#elif defined(CONFIG_ARM64_MTE) +#ifdef CONFIG_ARM64_MTE /* * The mte_zero_clear_page_tags() implementation uses DC GZVA, which relies on - * TBI being enabled at EL1. + * TBI being enabled at EL1. TCMA1 is needed to treat accesses with the + * match-all tag (0xF) as Tag Unchecked, irrespective of the SCTLR_EL1.TCF + * setting. */ -#define TCR_MTE_FLAGS TCR_EL1_TBI1 | TCR_EL1_TBID1 +#define TCR_MTE_FLAGS TCR_EL1_TCMA1 | TCR_EL1_TBI1 | TCR_EL1_TBID1 #else #define TCR_MTE_FLAGS 0 #endif From ab26d9c85554c4ff1d95ca8341522880ed9219d6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 28 Jan 2026 10:55:34 +0100 Subject: [PATCH 36/43] perf/cxlpmu: Replace IRQF_ONESHOT with IRQF_NO_THREAD Passing IRQF_ONESHOT ensures that the interrupt source is masked until the secondary (threaded) handler is done. If only a primary handler is used then the flag makes no sense because the interrupt can not fire (again) while its handler is running. The flag also disallows force-threading of the primary handler and the irq-core will warn about this. The intention here was probably not allowing forced-threading. Replace IRQF_ONESHOT with IRQF_NO_THREAD. Reviewed-by: Jonathan Cameron Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Will Deacon --- drivers/perf/cxl_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index d094030220bf..68a54d97d2a8 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -877,7 +877,7 @@ static int cxl_pmu_probe(struct device *dev) if (!irq_name) return -ENOMEM; - rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED | IRQF_ONESHOT, + rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED | IRQF_NO_THREAD, irq_name, info); if (rc) return rc; From 96e004b4bdf9029d137a1b06de1606ff6e263ab8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 27 Jan 2026 16:16:14 +0000 Subject: [PATCH 37/43] kselftest/arm64: Add a no-SVE loop after SVE in fp-pidbench Some applications use SVE intermittently, one common case being where SVE is used during statup (eg, by ld.so) but then rarely if ever during the main application runtime. Add a repeat of the no SVE loop after we've done the SVE loops to fp-pidbench to capture results for that. Signed-off-by: Mark Brown Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/fp-pidbench.S | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/arm64/fp/fp-pidbench.S b/tools/testing/selftests/arm64/fp/fp-pidbench.S index 73830f6bc99b..aeeadc7873dc 100644 --- a/tools/testing/selftests/arm64/fp/fp-pidbench.S +++ b/tools/testing/selftests/arm64/fp/fp-pidbench.S @@ -63,6 +63,10 @@ function _start puts "SVE used per syscall: " test_loop "rdvl x0, #8" + // Test non-SVE execution after SVE + puts "No SVE after SVE: " + test_loop + // And we're done out: mov x0, #0 From b661d753ce2ee951558db1f2c7b97f32d9431966 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 27 Jan 2026 16:16:15 +0000 Subject: [PATCH 38/43] kselftest/arm64: Raise default number of loops in fp-pidbench When fp-pidbench was originally written SVE hardware was not widely available so it was useful to run it in emulation and the default number of loops was set very low, running for less than a second on actual hardware. Now that SVE hardware is reasonably available it is very much less interesting to use emulation, bump the default number of loops up to even out a bit of the noise on real systems. On the machine I have to hand this now takes about 15s which is still a toy microbenchmark but perhaps a bit more useful. Signed-off-by: Mark Brown Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/fp-pidbench.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/fp/fp-pidbench.S b/tools/testing/selftests/arm64/fp/fp-pidbench.S index aeeadc7873dc..881dfa3b342e 100644 --- a/tools/testing/selftests/arm64/fp/fp-pidbench.S +++ b/tools/testing/selftests/arm64/fp/fp-pidbench.S @@ -33,7 +33,7 @@ function _start puts "Iterations per test: " mov x20, #10000 - lsl x20, x20, #8 + lsl x20, x20, #12 mov x0, x20 bl putdec puts "\n" From 1f3b950492db411e6c30ee0076b61ef2694c100a Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Tue, 27 Jan 2026 13:39:26 +0000 Subject: [PATCH 39/43] arm64: poe: fix stale POR_EL0 values for ptrace If a process wrote to POR_EL0 and then crashed before a context switch happened, the coredump would contain an incorrect value for POR_EL0. The value read in poe_get() would be a stale value left in thread.por_el0. Fix this by reading the value from the system register, if the target thread is the current thread. This matches what gcs/fpsimd do. Fixes: 175198199262 ("arm64/ptrace: add support for FEAT_POE") Reported-by: David Spickett Cc: stable@vger.kernel.org Signed-off-by: Joey Gouly Cc: Kevin Brodsky Cc: Mark Rutland Reviewed-by: Kevin Brodsky Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/kernel/ptrace.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index b9bdd83fbbca..8a14b86cd066 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1486,6 +1486,9 @@ static int poe_get(struct task_struct *target, if (!system_supports_poe()) return -EINVAL; + if (target == current) + current->thread.por_el0 = read_sysreg_s(SYS_POR_EL0); + return membuf_write(&to, &target->thread.por_el0, sizeof(target->thread.por_el0)); } From bb0c99e08ab9aa6d04b40cb63c72db9950d51749 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 30 Jan 2026 14:28:24 +0100 Subject: [PATCH 40/43] arm64: Fix non-atomic __READ_ONCE() with CONFIG_LTO=y The implementation of __READ_ONCE() under CONFIG_LTO=y incorrectly qualified the fallback "once" access for types larger than 8 bytes, which are not atomic but should still happen "once" and suppress common compiler optimizations. The cast `volatile typeof(__x)` applied the volatile qualifier to the pointer type itself rather than the pointee. This created a volatile pointer to a non-volatile type, which violated __READ_ONCE() semantics. Fix this by casting to `volatile typeof(*__x) *`. With a defconfig + LTO + debug options build, we see the following functions to be affected: xen_manage_runstate_time (884 -> 944 bytes) xen_steal_clock (248 -> 340 bytes) ^-- use __READ_ONCE() to load vcpu_runstate_info structs Fixes: e35123d83ee3 ("arm64: lto: Strengthen READ_ONCE() to acquire when CONFIG_LTO=y") Cc: stable@vger.kernel.org Reviewed-by: Boqun Feng Signed-off-by: Marco Elver Tested-by: David Laight Signed-off-by: Will Deacon --- arch/arm64/include/asm/rwonce.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/rwonce.h b/arch/arm64/include/asm/rwonce.h index 78beceec10cd..fc0fb42b0b64 100644 --- a/arch/arm64/include/asm/rwonce.h +++ b/arch/arm64/include/asm/rwonce.h @@ -58,7 +58,7 @@ default: \ atomic = 0; \ } \ - atomic ? (typeof(*__x))__u.__val : (*(volatile typeof(__x))__x);\ + atomic ? (typeof(*__x))__u.__val : (*(volatile typeof(*__x) *)__x);\ }) #endif /* !BUILD_VDSO */ From 53c998527ffa60f9deda8974a11ad39790684159 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 2 Feb 2026 10:32:51 -0800 Subject: [PATCH 41/43] arm64/gcs: Fix error handling in arch_set_shadow_stack_status() alloc_gcs() returns an error-encoded pointer on failure, which comes from do_mmap(), not NULL. The current NULL check fails to detect errors, which could lead to using an invalid GCS address. Use IS_ERR_VALUE() to properly detect errors, consistent with the check in gcs_alloc_thread_stack(). Fixes: b57180c75c7e ("arm64/gcs: Implement shadow stack prctl() interface") Reviewed-by: Mark Brown Signed-off-by: Breno Leitao Signed-off-by: Will Deacon --- arch/arm64/mm/gcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/gcs.c b/arch/arm64/mm/gcs.c index 6e93f78de79b..04a23a497f20 100644 --- a/arch/arm64/mm/gcs.c +++ b/arch/arm64/mm/gcs.c @@ -199,8 +199,8 @@ int arch_set_shadow_stack_status(struct task_struct *task, unsigned long arg) size = gcs_size(0); gcs = alloc_gcs(0, size); - if (!gcs) - return -ENOMEM; + if (IS_ERR_VALUE(gcs)) + return gcs; task->thread.gcspr_el0 = gcs + size - sizeof(u64); task->thread.gcs_base = gcs; From 283182c1c239f6873d1a50e9e710c1a699f2256b Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 3 Feb 2026 14:40:43 +0000 Subject: [PATCH 42/43] perf: arm_spe: Properly set hw.state on failures When arm_spe_pmu_next_off() fails to calculate a valid limit, it returns zero to indicate that tracing should not start. However, the caller arm_spe_perf_aux_output_begin() does not propagate this failure by updating hwc->state, cause the error to be silently ignored by upper layers. Because hwc->state remains zero after a failure, arm_spe_pmu_start() continues to programs filter registers unnecessarily. The driver still reports success to the perf core, so the core assumes the SPE event was enabled and proceeds to enable other events. This breaks event group semantics: SPE is already stopped while other events in the same group are enabled. Fix this by updating arm_spe_perf_aux_output_begin() to return a status code indicating success (0) or failure (-EIO). Both the interrupt handler and arm_spe_pmu_start() check the return value and call arm_spe_pmu_stop() to set PERF_HES_STOPPED in hwc->state. In the interrupt handler, the period (e.g., period_left) needs to be updated, so PERF_EF_UPDATE is passed to arm_spe_pmu_stop(). When the error occurs during event start, the trace unit is not yet enabled, so a flag '0' is used to drain buffer and update state only. Fixes: d5d9696b0380 ("drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension") Signed-off-by: Leo Yan Signed-off-by: Will Deacon --- drivers/perf/arm_spe_pmu.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c index 4801115f2b54..5410fb7428d0 100644 --- a/drivers/perf/arm_spe_pmu.c +++ b/drivers/perf/arm_spe_pmu.c @@ -106,6 +106,8 @@ struct arm_spe_pmu { /* Keep track of our dynamic hotplug state */ static enum cpuhp_state arm_spe_pmu_online; +static void arm_spe_pmu_stop(struct perf_event *event, int flags); + enum arm_spe_pmu_buf_fault_action { SPE_PMU_BUF_FAULT_ACT_SPURIOUS, SPE_PMU_BUF_FAULT_ACT_FATAL, @@ -607,8 +609,8 @@ static u64 arm_spe_pmu_next_off(struct perf_output_handle *handle) return limit; } -static void arm_spe_perf_aux_output_begin(struct perf_output_handle *handle, - struct perf_event *event) +static int arm_spe_perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event) { u64 base, limit; struct arm_spe_pmu_buf *buf; @@ -622,7 +624,6 @@ static void arm_spe_perf_aux_output_begin(struct perf_output_handle *handle, /* Start a new aux session */ buf = perf_aux_output_begin(handle, event); if (!buf) { - event->hw.state |= PERF_HES_STOPPED; /* * We still need to clear the limit pointer, since the * profiler might only be disabled by virtue of a fault. @@ -642,6 +643,7 @@ static void arm_spe_perf_aux_output_begin(struct perf_output_handle *handle, out_write_limit: write_sysreg_s(limit, SYS_PMBLIMITR_EL1); + return (limit & PMBLIMITR_EL1_E) ? 0 : -EIO; } static void arm_spe_perf_aux_output_end(struct perf_output_handle *handle) @@ -781,7 +783,10 @@ static irqreturn_t arm_spe_pmu_irq_handler(int irq, void *dev) * when we get to it. */ if (!(handle->aux_flags & PERF_AUX_FLAG_TRUNCATED)) { - arm_spe_perf_aux_output_begin(handle, event); + if (arm_spe_perf_aux_output_begin(handle, event)) { + arm_spe_pmu_stop(event, PERF_EF_UPDATE); + break; + } isb(); } break; @@ -880,9 +885,10 @@ static void arm_spe_pmu_start(struct perf_event *event, int flags) struct perf_output_handle *handle = this_cpu_ptr(spe_pmu->handle); hwc->state = 0; - arm_spe_perf_aux_output_begin(handle, event); - if (hwc->state) + if (arm_spe_perf_aux_output_begin(handle, event)) { + arm_spe_pmu_stop(event, 0); return; + } reg = arm_spe_event_to_pmsfcr(event); write_sysreg_s(reg, SYS_PMSFCR_EL1); From 36c0de02575ce59dfd879eb4ef63d53a68bbf9ce Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Tue, 3 Feb 2026 14:07:29 +0000 Subject: [PATCH 43/43] perf/arm-cmn: Reject unsupported hardware configurations So far we've been fairly lax about accepting both unknown CMN models (at least with a warning), and unknown revisions of those which we do know, as although things do frequently change between releases, typically enough remains the same to be somewhat useful for at least some basic bringup checks. However, we also make assumptions of the maximum supported sizes and numbers of things in various places, and there's no guarantee that something new might not be bigger and lead to nasty array overflows. Make sure we only try to run on things that actually match our assumptions and so will not risk memory corruption. We have at least always failed on completely unknown node types, so update that error message for clarity and consistency too. Cc: stable@vger.kernel.org Fixes: 7819e05a0dce ("perf/arm-cmn: Revamp model detection") Reviewed-by: Ilkka Koskinen Signed-off-by: Robin Murphy Signed-off-by: Will Deacon --- drivers/perf/arm-cmn.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 651edd73bfcb..4fbafc4b7984 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -2422,6 +2422,15 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) arm_cmn_init_node_info(cmn, reg & CMN_CHILD_NODE_ADDR, dn); dn->portid_bits = xp->portid_bits; dn->deviceid_bits = xp->deviceid_bits; + /* + * Logical IDs are assigned from 0 per node type, so as + * soon as we see one bigger than expected, we can assume + * there are more than we can cope with. + */ + if (dn->logid > CMN_MAX_NODES_PER_EVENT) { + dev_err(cmn->dev, "Node ID invalid for supported CMN versions: %d\n", dn->logid); + return -ENODEV; + } switch (dn->type) { case CMN_TYPE_DTC: @@ -2471,7 +2480,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) break; /* Something has gone horribly wrong */ default: - dev_err(cmn->dev, "invalid device node type: 0x%x\n", dn->type); + dev_err(cmn->dev, "Device node type invalid for supported CMN versions: 0x%x\n", dn->type); return -ENODEV; } } @@ -2499,6 +2508,10 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) cmn->mesh_x = cmn->num_xps; cmn->mesh_y = cmn->num_xps / cmn->mesh_x; + if (max(cmn->mesh_x, cmn->mesh_y) > CMN_MAX_DIMENSION) { + dev_err(cmn->dev, "Mesh size invalid for supported CMN versions: %dx%d\n", cmn->mesh_x, cmn->mesh_y); + return -ENODEV; + } /* 1x1 config plays havoc with XP event encodings */ if (cmn->num_xps == 1) dev_warn(cmn->dev, "1x1 config not fully supported, translate XP events manually\n");