Skip to content

Commit 8c83886

Browse files
committed
Merge branch 'bits/220-tso' into asahi-wip
2 parents d99ac96 + 9c5b7b5 commit 8c83886

File tree

15 files changed

+307
-5
lines changed

15 files changed

+307
-5
lines changed

arch/arm64/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ config KASAN_SHADOW_OFFSET
443443
config UNWIND_TABLES
444444
bool
445445

446+
config ARM64_ACTLR_STATE
447+
bool
448+
446449
source "arch/arm64/Kconfig.platforms"
447450

448451
menu "Kernel Features"
@@ -2268,6 +2271,16 @@ config ARM64_DEBUG_PRIORITY_MASKING
22682271
If unsure, say N
22692272
endif # ARM64_PSEUDO_NMI
22702273

2274+
config ARM64_MEMORY_MODEL_CONTROL
2275+
bool "Runtime memory model control"
2276+
select ARM64_ACTLR_STATE
2277+
help
2278+
Some ARM64 CPUs support runtime switching of the CPU memory
2279+
model, which can be useful to emulate other CPU architectures
2280+
which have different memory models. Say Y to enable support
2281+
for the PR_SET_MEM_MODEL/PR_GET_MEM_MODEL prctl() calls on
2282+
CPUs with this feature.
2283+
22712284
config RELOCATABLE
22722285
bool "Build a relocatable kernel image" if EXPERT
22732286
select ARCH_HAS_RELR
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#ifndef __ASM_APPLE_CPUFEATURES_H
4+
#define __ASM_APPLE_CPUFEATURES_H
5+
6+
#include <linux/bits.h>
7+
#include <asm/sysreg.h>
8+
9+
#define AIDR_APPLE_TSO_SHIFT 9
10+
#define AIDR_APPLE_TSO BIT(9)
11+
12+
#define ACTLR_APPLE_TSO_SHIFT 1
13+
#define ACTLR_APPLE_TSO BIT(1)
14+
15+
#endif

arch/arm64/include/asm/cpufeature.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,12 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
955955
return 8;
956956
}
957957

958+
static __always_inline bool system_has_actlr_state(void)
959+
{
960+
return IS_ENABLED(CONFIG_ARM64_ACTLR_STATE) &&
961+
alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE);
962+
}
963+
958964
s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
959965
struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
960966

@@ -1078,6 +1084,10 @@ static inline bool cpu_has_lpa2(void)
10781084
#endif
10791085
}
10801086

1087+
void __init init_cpucap_indirect_list_impdef(void);
1088+
void __init init_cpucap_indirect_list_from_array(const struct arm64_cpu_capabilities *caps);
1089+
bool cpufeature_matches(u64 reg, const struct arm64_cpu_capabilities *entry);
1090+
10811091
#endif /* __ASSEMBLY__ */
10821092

10831093
#endif

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
103103
{
104104
if (!vcpu_has_run_once(vcpu))
105105
vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS;
106+
if (IS_ENABLED(CONFIG_ARM64_ACTLR_STATE) && (
107+
alternative_has_cap_unlikely(ARM64_HAS_ACTLR_VIRT) ||
108+
alternative_has_cap_unlikely(ARM64_HAS_ACTLR_VIRT_APPLE)
109+
))
110+
vcpu->arch.hcr_el2 &= ~HCR_TACR;
106111

107112
/*
108113
* For non-FWB CPUs, we trap VM ops (HCR_EL2.TVM) until M+C

arch/arm64/include/asm/processor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ struct thread_struct {
194194
u64 gcs_base;
195195
u64 gcs_size;
196196
#endif
197+
#ifdef CONFIG_ARM64_ACTLR_STATE
198+
u64 actlr;
199+
#endif
197200
};
198201

199202
static inline unsigned int thread_get_vl(struct thread_struct *thread,

arch/arm64/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
3434
cpufeature.o alternative.o cacheinfo.o \
3535
smp.o smp_spin_table.o topology.o smccc-call.o \
3636
syscall.o proton-pack.o idle.o patching.o pi/ \
37+
cpufeature_impdef.o \
3738
rsi.o jump_label.o
3839

3940
obj-$(CONFIG_COMPAT) += sys32.o signal32.o \

arch/arm64/kernel/cpufeature.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
10801080
extern const struct arm64_cpu_capabilities arm64_errata[];
10811081
static const struct arm64_cpu_capabilities arm64_features[];
10821082

1083-
static void __init
1083+
void __init
10841084
init_cpucap_indirect_list_from_array(const struct arm64_cpu_capabilities *caps)
10851085
{
10861086
for (; caps->matches; caps++) {
@@ -1592,8 +1592,8 @@ has_always(const struct arm64_cpu_capabilities *entry, int scope)
15921592
return true;
15931593
}
15941594

1595-
static bool
1596-
feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
1595+
bool
1596+
cpufeature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
15971597
{
15981598
int val, min, max;
15991599
u64 tmp;
@@ -1646,14 +1646,14 @@ has_user_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
16461646
if (!mask)
16471647
return false;
16481648

1649-
return feature_matches(val, entry);
1649+
return cpufeature_matches(val, entry);
16501650
}
16511651

16521652
static bool
16531653
has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
16541654
{
16551655
u64 val = read_scoped_sysreg(entry, scope);
1656-
return feature_matches(val, entry);
1656+
return cpufeature_matches(val, entry);
16571657
}
16581658

16591659
const struct cpumask *system_32bit_el0_cpumask(void)
@@ -3829,6 +3829,7 @@ void __init setup_boot_cpu_features(void)
38293829
* handle the boot CPU.
38303830
*/
38313831
init_cpucap_indirect_list();
3832+
init_cpucap_indirect_list_impdef();
38323833

38333834
/*
38343835
* Detect broken pseudo-NMI. Must be called _before_ the call to
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Contains implementation-defined CPU feature definitions.
4+
*/
5+
6+
#define pr_fmt(fmt) "CPU features: " fmt
7+
8+
#include <asm/cpufeature.h>
9+
#include <asm/apple_cpufeature.h>
10+
#include <linux/irqflags.h>
11+
#include <linux/preempt.h>
12+
#include <linux/printk.h>
13+
14+
#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL
15+
static bool has_apple_feature(const struct arm64_cpu_capabilities *entry, int scope)
16+
{
17+
u64 val;
18+
WARN_ON(scope == SCOPE_LOCAL_CPU && preemptible());
19+
20+
if (read_cpuid_implementor() != ARM_CPU_IMP_APPLE)
21+
return false;
22+
23+
val = read_sysreg(aidr_el1);
24+
return cpufeature_matches(val, entry);
25+
}
26+
27+
static bool has_apple_tso(const struct arm64_cpu_capabilities *entry, int scope)
28+
{
29+
u64 val;
30+
31+
if (!has_apple_feature(entry, scope))
32+
return false;
33+
34+
/*
35+
* KVM and old versions of the macOS hypervisor will advertise TSO in
36+
* AIDR_EL1, but then ignore writes to ACTLR_EL1. Test that the bit is
37+
* actually writable before enabling TSO.
38+
*/
39+
40+
val = read_sysreg(actlr_el1);
41+
write_sysreg(val ^ ACTLR_APPLE_TSO, actlr_el1);
42+
if (!((val ^ read_sysreg(actlr_el1)) & ACTLR_APPLE_TSO)) {
43+
pr_info_once("CPU advertises Apple TSO but it is broken, ignoring\n");
44+
return false;
45+
}
46+
47+
write_sysreg(val, actlr_el1);
48+
return true;
49+
}
50+
51+
static bool has_tso_fixed(const struct arm64_cpu_capabilities *entry, int scope)
52+
{
53+
/* List of CPUs that always use the TSO memory model */
54+
static const struct midr_range fixed_tso_list[] = {
55+
MIDR_ALL_VERSIONS(MIDR_NVIDIA_DENVER),
56+
MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL),
57+
MIDR_ALL_VERSIONS(MIDR_FUJITSU_A64FX),
58+
{ /* sentinel */ }
59+
};
60+
61+
return is_midr_in_range_list(fixed_tso_list);
62+
}
63+
#endif
64+
65+
static bool has_apple_actlr_virt_impdef(const struct arm64_cpu_capabilities *entry, int scope)
66+
{
67+
u64 midr = read_cpuid_id() & MIDR_CPU_MODEL_MASK;
68+
69+
return midr >= MIDR_APPLE_M1_ICESTORM && midr <= MIDR_APPLE_M1_FIRESTORM_MAX;
70+
}
71+
72+
static bool has_apple_actlr_virt(const struct arm64_cpu_capabilities *entry, int scope)
73+
{
74+
u64 midr = read_cpuid_id() & MIDR_CPU_MODEL_MASK;
75+
76+
return midr >= MIDR_APPLE_M2_BLIZZARD && midr <= MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, 0xfff);
77+
}
78+
79+
static const struct arm64_cpu_capabilities arm64_impdef_features[] = {
80+
#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL
81+
{
82+
.desc = "TSO memory model (Apple)",
83+
.capability = ARM64_HAS_TSO_APPLE,
84+
.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
85+
.matches = has_apple_tso,
86+
.field_pos = AIDR_APPLE_TSO_SHIFT,
87+
.field_width = 1,
88+
.sign = FTR_UNSIGNED,
89+
.min_field_value = 1,
90+
.max_field_value = 1,
91+
},
92+
{
93+
.desc = "TSO memory model (Fixed)",
94+
.capability = ARM64_HAS_TSO_FIXED,
95+
.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
96+
.matches = has_tso_fixed,
97+
},
98+
#endif
99+
{
100+
.desc = "ACTLR virtualization (IMPDEF, Apple)",
101+
.capability = ARM64_HAS_ACTLR_VIRT_APPLE,
102+
.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
103+
.matches = has_apple_actlr_virt_impdef,
104+
},
105+
{
106+
.desc = "ACTLR virtualization (architectural?)",
107+
.capability = ARM64_HAS_ACTLR_VIRT,
108+
.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
109+
.matches = has_apple_actlr_virt,
110+
},
111+
{},
112+
};
113+
114+
void __init init_cpucap_indirect_list_impdef(void)
115+
{
116+
init_cpucap_indirect_list_from_array(arm64_impdef_features);
117+
}

arch/arm64/kernel/process.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@
4141
#include <linux/thread_info.h>
4242
#include <linux/prctl.h>
4343
#include <linux/stacktrace.h>
44+
#include <linux/memory_ordering_model.h>
4445

4546
#include <asm/alternative.h>
47+
#include <asm/apple_cpufeature.h>
4648
#include <asm/arch_timer.h>
4749
#include <asm/compat.h>
4850
#include <asm/cpufeature.h>
@@ -442,6 +444,11 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
442444
if (system_supports_poe())
443445
p->thread.por_el0 = read_sysreg_s(SYS_POR_EL0);
444446

447+
#ifdef CONFIG_ARM64_ACTLR_STATE
448+
if (system_has_actlr_state())
449+
p->thread.actlr = read_sysreg(actlr_el1);
450+
#endif
451+
445452
if (stack_start) {
446453
if (is_compat_thread(task_thread_info(p)))
447454
childregs->compat_sp = stack_start;
@@ -699,6 +706,65 @@ void update_sctlr_el1(u64 sctlr)
699706
isb();
700707
}
701708

709+
#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL
710+
int arch_prctl_mem_model_get(struct task_struct *t)
711+
{
712+
if (alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE) &&
713+
t->thread.actlr & ACTLR_APPLE_TSO)
714+
return PR_SET_MEM_MODEL_TSO;
715+
716+
return PR_SET_MEM_MODEL_DEFAULT;
717+
}
718+
719+
int arch_prctl_mem_model_set(struct task_struct *t, unsigned long val)
720+
{
721+
if (alternative_has_cap_unlikely(ARM64_HAS_TSO_FIXED) &&
722+
val == PR_SET_MEM_MODEL_TSO)
723+
return 0;
724+
725+
if (alternative_has_cap_unlikely(ARM64_HAS_TSO_APPLE)) {
726+
WARN_ON(!system_has_actlr_state());
727+
728+
switch (val) {
729+
case PR_SET_MEM_MODEL_TSO:
730+
t->thread.actlr |= ACTLR_APPLE_TSO;
731+
break;
732+
case PR_SET_MEM_MODEL_DEFAULT:
733+
t->thread.actlr &= ~ACTLR_APPLE_TSO;
734+
break;
735+
default:
736+
return -EINVAL;
737+
}
738+
write_sysreg(t->thread.actlr, actlr_el1);
739+
return 0;
740+
}
741+
742+
if (val == PR_SET_MEM_MODEL_DEFAULT)
743+
return 0;
744+
745+
return -EINVAL;
746+
}
747+
#endif
748+
749+
#ifdef CONFIG_ARM64_ACTLR_STATE
750+
/*
751+
* IMPDEF control register ACTLR_EL1 handling. Some CPUs use this to
752+
* expose features that can be controlled by userspace.
753+
*/
754+
static void actlr_thread_switch(struct task_struct *next)
755+
{
756+
if (!system_has_actlr_state())
757+
return;
758+
759+
current->thread.actlr = read_sysreg(actlr_el1);
760+
write_sysreg(next->thread.actlr, actlr_el1);
761+
}
762+
#else
763+
static inline void actlr_thread_switch(struct task_struct *next)
764+
{
765+
}
766+
#endif
767+
702768
/*
703769
* Thread switching.
704770
*/
@@ -718,6 +784,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
718784
ptrauth_thread_switch_user(next);
719785
permission_overlay_switch(next);
720786
gcs_thread_switch(next);
787+
actlr_thread_switch(next);
721788

722789
/*
723790
* Complete any pending TLB or cache maintenance on this CPU in case the
@@ -840,6 +907,10 @@ void arch_setup_new_exec(void)
840907
arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS,
841908
PR_SPEC_ENABLE);
842909
}
910+
911+
#ifdef CONFIG_ARM64_MEMORY_MODEL_CONTROL
912+
arch_prctl_mem_model_set(current, PR_SET_MEM_MODEL_DEFAULT);
913+
#endif
843914
}
844915

845916
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI

arch/arm64/kernel/setup.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
368368
*/
369369
init_task.thread_info.ttbr0 = phys_to_ttbr(__pa_symbol(reserved_pg_dir));
370370
#endif
371+
#ifdef CONFIG_ARM64_ACTLR_STATE
372+
/* Store the boot CPU ACTLR_EL1 value as the default. This will only
373+
* be actually restored during context switching iff the platform is
374+
* known to use ACTLR_EL1 for exposable features and its layout is
375+
* known to be the same on all CPUs.
376+
*/
377+
init_task.thread.actlr = read_sysreg(actlr_el1);
378+
#endif
371379

372380
if (boot_args[1] || boot_args[2] || boot_args[3]) {
373381
pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"

0 commit comments

Comments
 (0)