18e06d04fSJames Morse /* SPDX-License-Identifier: GPL-2.0 */
28e06d04fSJames Morse /* Copyright (C) 2025 Arm Ltd. */
38e06d04fSJames Morse
48e06d04fSJames Morse #ifndef __ASM__MPAM_H
58e06d04fSJames Morse #define __ASM__MPAM_H
68e06d04fSJames Morse
7*6789fb99SJames Morse #include <linux/arm_mpam.h>
82cf9ca3fSJames Morse #include <linux/bitfield.h>
98e06d04fSJames Morse #include <linux/jump_label.h>
108e06d04fSJames Morse #include <linux/percpu.h>
118e06d04fSJames Morse #include <linux/sched.h>
128e06d04fSJames Morse
138e06d04fSJames Morse #include <asm/sysreg.h>
148e06d04fSJames Morse
158e06d04fSJames Morse DECLARE_STATIC_KEY_FALSE(mpam_enabled);
168e06d04fSJames Morse DECLARE_PER_CPU(u64, arm64_mpam_default);
178e06d04fSJames Morse DECLARE_PER_CPU(u64, arm64_mpam_current);
188e06d04fSJames Morse
198e06d04fSJames Morse /*
208e06d04fSJames Morse * The value of the MPAM0_EL1 sysreg when a task is in resctrl's default group.
218e06d04fSJames Morse * This is used by the context switch code to use the resctrl CPU property
228e06d04fSJames Morse * instead. The value is modified when CDP is enabled/disabled by mounting
238e06d04fSJames Morse * the resctrl filesystem.
248e06d04fSJames Morse */
258e06d04fSJames Morse extern u64 arm64_mpam_global_default;
268e06d04fSJames Morse
272cf9ca3fSJames Morse #ifdef CONFIG_ARM64_MPAM
__mpam_regval(u16 partid_d,u16 partid_i,u8 pmg_d,u8 pmg_i)282cf9ca3fSJames Morse static inline u64 __mpam_regval(u16 partid_d, u16 partid_i, u8 pmg_d, u8 pmg_i)
292cf9ca3fSJames Morse {
302cf9ca3fSJames Morse return FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d) |
312cf9ca3fSJames Morse FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i) |
322cf9ca3fSJames Morse FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d) |
332cf9ca3fSJames Morse FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
342cf9ca3fSJames Morse }
352cf9ca3fSJames Morse
mpam_set_cpu_defaults(int cpu,u16 partid_d,u16 partid_i,u8 pmg_d,u8 pmg_i)362cf9ca3fSJames Morse static inline void mpam_set_cpu_defaults(int cpu, u16 partid_d, u16 partid_i,
372cf9ca3fSJames Morse u8 pmg_d, u8 pmg_i)
382cf9ca3fSJames Morse {
392cf9ca3fSJames Morse u64 default_val = __mpam_regval(partid_d, partid_i, pmg_d, pmg_i);
402cf9ca3fSJames Morse
412cf9ca3fSJames Morse WRITE_ONCE(per_cpu(arm64_mpam_default, cpu), default_val);
422cf9ca3fSJames Morse }
432cf9ca3fSJames Morse
448e06d04fSJames Morse /*
458e06d04fSJames Morse * The resctrl filesystem writes to the partid/pmg values for threads and CPUs,
468e06d04fSJames Morse * which may race with reads in mpam_thread_switch(). Ensure only one of the old
478e06d04fSJames Morse * or new values are used. Particular care should be taken with the pmg field as
488e06d04fSJames Morse * mpam_thread_switch() may read a partid and pmg that don't match, causing this
498e06d04fSJames Morse * value to be stored with cache allocations, despite being considered 'free' by
508e06d04fSJames Morse * resctrl.
518e06d04fSJames Morse */
mpam_get_regval(struct task_struct * tsk)528e06d04fSJames Morse static inline u64 mpam_get_regval(struct task_struct *tsk)
538e06d04fSJames Morse {
548e06d04fSJames Morse return READ_ONCE(task_thread_info(tsk)->mpam_partid_pmg);
558e06d04fSJames Morse }
568e06d04fSJames Morse
mpam_set_task_partid_pmg(struct task_struct * tsk,u16 partid_d,u16 partid_i,u8 pmg_d,u8 pmg_i)572cf9ca3fSJames Morse static inline void mpam_set_task_partid_pmg(struct task_struct *tsk,
582cf9ca3fSJames Morse u16 partid_d, u16 partid_i,
592cf9ca3fSJames Morse u8 pmg_d, u8 pmg_i)
602cf9ca3fSJames Morse {
612cf9ca3fSJames Morse u64 regval = __mpam_regval(partid_d, partid_i, pmg_d, pmg_i);
622cf9ca3fSJames Morse
632cf9ca3fSJames Morse WRITE_ONCE(task_thread_info(tsk)->mpam_partid_pmg, regval);
642cf9ca3fSJames Morse }
652cf9ca3fSJames Morse
mpam_thread_switch(struct task_struct * tsk)668e06d04fSJames Morse static inline void mpam_thread_switch(struct task_struct *tsk)
678e06d04fSJames Morse {
688e06d04fSJames Morse u64 oldregval;
698e06d04fSJames Morse int cpu = smp_processor_id();
708e06d04fSJames Morse u64 regval = mpam_get_regval(tsk);
718e06d04fSJames Morse
728e06d04fSJames Morse if (!static_branch_likely(&mpam_enabled))
738e06d04fSJames Morse return;
748e06d04fSJames Morse
758e06d04fSJames Morse if (regval == READ_ONCE(arm64_mpam_global_default))
768e06d04fSJames Morse regval = READ_ONCE(per_cpu(arm64_mpam_default, cpu));
778e06d04fSJames Morse
788e06d04fSJames Morse oldregval = READ_ONCE(per_cpu(arm64_mpam_current, cpu));
798e06d04fSJames Morse if (oldregval == regval)
808e06d04fSJames Morse return;
818e06d04fSJames Morse
828e06d04fSJames Morse write_sysreg_s(regval | MPAM1_EL1_MPAMEN, SYS_MPAM1_EL1);
8337fe0f98SBen Horgan if (system_supports_sme())
8437fe0f98SBen Horgan write_sysreg_s(regval & (MPAMSM_EL1_PARTID_D | MPAMSM_EL1_PMG_D), SYS_MPAMSM_EL1);
858e06d04fSJames Morse isb();
868e06d04fSJames Morse
878e06d04fSJames Morse /* Synchronising the EL0 write is left until the ERET to EL0 */
888e06d04fSJames Morse write_sysreg_s(regval, SYS_MPAM0_EL1);
898e06d04fSJames Morse
908e06d04fSJames Morse WRITE_ONCE(per_cpu(arm64_mpam_current, cpu), regval);
918e06d04fSJames Morse }
928e06d04fSJames Morse #else
mpam_thread_switch(struct task_struct * tsk)938e06d04fSJames Morse static inline void mpam_thread_switch(struct task_struct *tsk) {}
948e06d04fSJames Morse #endif /* CONFIG_ARM64_MPAM */
958e06d04fSJames Morse
968e06d04fSJames Morse #endif /* __ASM__MPAM_H */
97