/* * (Note: in this discussion, statements about FPSIMD apply equally to SVE.) * * In order to reduce the number of times the FPSIMD state is needlessly saved * and restored, we need to keep track of two things: * (a) for each task, we need to remember which CPU was the last one to have * the task's FPSIMD state loaded into its FPSIMD registers; * (b) for each CPU, we need to remember which task's userland FPSIMD state has * been loaded into its FPSIMD registers most recently, or whether it has * been used to perform kernel mode NEON in the meantime. * * For (a), we add a fpsimd_cpu field to thread_struct, which gets updated to * the id of the current CPU every time the state is loaded onto a CPU. For (b), * we add the per-cpu variable 'fpsimd_last_state' (below), which contains the * address of the userland FPSIMD state of the task that was loaded onto the CPU * the most recently, or NULL if kernel mode NEON has been performed after that. * * With this in place, we no longer have to restore the next FPSIMD state right * when switching between tasks. Instead, we can defer this check to userland * resume, at which time we verify whether the CPU's fpsimd_last_state and the * task's fpsimd_cpu are still mutually in sync. If this is the case, we * can omit the FPSIMD restore. * * As an optimization, we use the thread_info flag TIF_FOREIGN_FPSTATE to * indicate whether or not the userland FPSIMD state of the current task is * present in the registers. The flag is set unless the FPSIMD registers of this * CPU currently contain the most recent userland FPSIMD state of the current * task. If the task is behaving as a VMM, then this is will be managed by * KVM which will clear it to indicate that the vcpu FPSIMD state is currently * loaded on the CPU, allowing the state to be saved if a FPSIMD-aware * softirq kicks in. Upon vcpu_put(), KVM will save the vcpu FP state and * flag the register state as invalid. * * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may be * called from softirq context, which will save the task's FPSIMD context back * to task_struct. To prevent this from racing with the manipulation of the * task's FPSIMD state from task context and thereby corrupting the state, it * is necessary to protect any manipulation of a task's fpsimd_state or * TIF_FOREIGN_FPSTATE flag with get_cpu_fpsimd_context(), which will suspend * softirq servicing entirely until put_cpu_fpsimd_context() is called. * * For a certain task, the sequence may look something like this: * - the task gets scheduled in; if both the task's fpsimd_cpu field * contains the id of the current CPU, and the CPU's fpsimd_last_state per-cpu * variable points to the task's fpsimd_state, the TIF_FOREIGN_FPSTATE flag is * cleared, otherwise it is set; * * - the task returns to userland; if TIF_FOREIGN_FPSTATE is set, the task's * userland FPSIMD state is copied from memory to the registers, the task's * fpsimd_cpu field is set to the id of the current CPU, the current * CPU's fpsimd_last_state pointer is set to this task's fpsimd_state and the * TIF_FOREIGN_FPSTATE flag is cleared; * * - the task executes an ordinary syscall; upon return to userland, the * TIF_FOREIGN_FPSTATE flag will still be cleared, so no FPSIMD state is * restored; * * - the task executes a syscall which executes some NEON instructions; this is * preceded by a call to kernel_neon_begin(), which copies the task's FPSIMD * register contents to memory, clears the fpsimd_last_state per-cpu variable * and sets the TIF_FOREIGN_FPSTATE flag; * * - the task gets preempted after kernel_neon_end() is called; as we have not * returned from the 2nd syscall yet, TIF_FOREIGN_FPSTATE is still set so * whatever is in the FPSIMD registers is not saved to memory, but discarded.
*/
/* * Claim ownership of the CPU FPSIMD context for use by the calling context. * * The caller may freely manipulate the FPSIMD context metadata until * put_cpu_fpsimd_context() is called. * * On RT kernels local_bh_disable() is not sufficient because it only * serializes soft interrupt related sections via a local lock, but stays * preemptible. Disabling preemption is the right choice here as bottom * half processing is always in thread context on RT kernels so it * implicitly prevents bottom half processing as well.
*/ staticvoid get_cpu_fpsimd_context(void)
{ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_bh_disable(); else
preempt_disable();
}
/* * Release the CPU FPSIMD context. * * Must be called from a context in which get_cpu_fpsimd_context() was * previously called, with no call to put_cpu_fpsimd_context() in the * meantime.
*/ staticvoid put_cpu_fpsimd_context(void)
{ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
local_bh_enable(); else
preempt_enable();
}
/* * TIF_SME controls whether a task can use SME without trapping while * in userspace, when TIF_SME is set then we must have storage * allocated in sve_state and sme_state to store the contents of both ZA * and the SVE registers for both streaming and non-streaming modes. * * If both SVCR.ZA and SVCR.SM are disabled then at any point we * may disable TIF_SME and reenable traps.
*/
/* * TIF_SVE controls whether a task can use SVE without trapping while * in userspace, and also (together with TIF_SME) the way a task's * FPSIMD/SVE state is stored in thread_struct. * * The kernel uses this flag to track whether a user task is actively * using SVE, and therefore whether full SVE register state needs to * be tracked. If not, the cheaper FPSIMD context handling code can * be used instead of the more costly SVE equivalents. * * * TIF_SVE or SVCR.SM set: * * The task can execute SVE instructions while in userspace without * trapping to the kernel. * * During any syscall, the kernel may optionally clear TIF_SVE and * discard the vector state except for the FPSIMD subset. * * * TIF_SVE clear: * * An attempt by the user task to execute an SVE instruction causes * do_sve_acc() to be called, which does some preparation and then * sets TIF_SVE. * * During any syscall, the kernel may optionally clear TIF_SVE and * discard the vector state except for the FPSIMD subset. * * The data will be stored in one of two formats: * * * FPSIMD only - FP_STATE_FPSIMD: * * When the FPSIMD only state stored task->thread.fp_type is set to * FP_STATE_FPSIMD, the FPSIMD registers V0-V31 are encoded in * task->thread.uw.fpsimd_state; bits [max : 128] for each of Z0-Z31 are * logically zero but not stored anywhere; P0-P15 and FFR are not * stored and have unspecified values from userspace's point of * view. For hygiene purposes, the kernel zeroes them on next use, * but userspace is discouraged from relying on this. * * task->thread.sve_state does not need to be non-NULL, valid or any * particular size: it must not be dereferenced and any data stored * there should be considered stale and not referenced. * * * SVE state - FP_STATE_SVE: * * When the full SVE state is stored task->thread.fp_type is set to * FP_STATE_SVE and Z0-Z31 (incorporating Vn in bits[127:0] or the * corresponding Zn), P0-P15 and FFR are encoded in in * task->thread.sve_state, formatted appropriately for vector * length task->thread.sve_vl or, if SVCR.SM is set, * task->thread.sme_vl. The storage for the vector registers in * task->thread.uw.fpsimd_state should be ignored. * * task->thread.sve_state must point to a valid buffer at least * sve_state_size(task) bytes in size. The data stored in * task->thread.uw.fpsimd_state.vregs should be considered stale * and not referenced. * * * FPSR and FPCR are always stored in task->thread.uw.fpsimd_state * irrespective of whether TIF_SVE is clear or set, since these are * not vector length dependent.
*/
/* * Update current's FPSIMD/SVE registers from thread_struct. * * This function should be called only when the FPSIMD/SVE state in * thread_struct is known to be up to date, when preparing to enter * userspace.
*/ staticvoid task_fpsimd_load(void)
{ bool restore_sve_regs = false; bool restore_ffr;
if (system_supports_sve() || system_supports_sme()) { switch (current->thread.fp_type) { case FP_STATE_FPSIMD: /* Stop tracking SVE for this task until next use. */
clear_thread_flag(TIF_SVE); break; case FP_STATE_SVE: if (!thread_sm_enabled(¤t->thread))
WARN_ON_ONCE(!test_and_set_thread_flag(TIF_SVE));
if (test_thread_flag(TIF_SVE))
sve_set_vq(sve_vq_from_vl(task_get_sve_vl(current)) - 1);
restore_sve_regs = true;
restore_ffr = true; break; default: /* * This indicates either a bug in * fpsimd_save_user_state() or memory corruption, we * should always record an explicit format * when we save. We always at least have the * memory allocated for FPSIMD registers so * try that and hope for the best.
*/
WARN_ON_ONCE(1);
clear_thread_flag(TIF_SVE); break;
}
}
/* Restore SME, override SVE register configuration if needed */ if (system_supports_sme()) { unsignedlong sme_vl = task_get_sme_vl(current);
/* Ensure VL is set up for restoring data */ if (test_thread_flag(TIF_SME))
sme_set_vq(sve_vq_from_vl(sme_vl) - 1);
write_sysreg_s(current->thread.svcr, SYS_SVCR);
if (thread_za_enabled(¤t->thread))
sme_load_state(current->thread.sme_state,
system_supports_sme2());
if (thread_sm_enabled(¤t->thread))
restore_ffr = system_supports_fa64();
}
if (system_supports_fpmr())
write_sysreg_s(current->thread.uw.fpmr, SYS_FPMR);
/* * Ensure FPSIMD/SVE storage in memory for the loaded context is up to * date with respect to the CPU registers. Note carefully that the * current context is the context last bound to the CPU stored in * last, if KVM is involved this may be the guest VM context rather * than the host thread for the VM pointed to by current. This means * that we must always reference the state storage via last rather * than via current, if we are saving KVM state then it will have * ensured that the type of registers to save is set in last->to_save.
*/ staticvoid fpsimd_save_user_state(void)
{ struct cpu_fp_state const *last =
this_cpu_ptr(&fpsimd_last_state); /* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */ bool save_sve_regs = false; bool save_ffr; unsignedint vl;
if (test_thread_flag(TIF_FOREIGN_FPSTATE)) return;
if (system_supports_fpmr())
*(last->fpmr) = read_sysreg_s(SYS_FPMR);
/* * Save SVE state if it is live. * * The syscall ABI discards live SVE state at syscall entry. When * entering a syscall, fpsimd_syscall_enter() sets to_save to * FP_STATE_FPSIMD to allow the SVE state to be lazily discarded until * either new SVE state is loaded+bound or fpsimd_syscall_exit() is * called prior to a return to userspace.
*/ if ((last->to_save == FP_STATE_CURRENT && test_thread_flag(TIF_SVE)) ||
last->to_save == FP_STATE_SVE) {
save_sve_regs = true;
save_ffr = true;
vl = last->sve_vl;
}
if (system_supports_sme()) {
u64 *svcr = last->svcr;
*svcr = read_sysreg_s(SYS_SVCR);
if (*svcr & SVCR_ZA_MASK)
sme_save_state(last->sme_state,
system_supports_sme2());
/* If we are in streaming mode override regular SVE. */ if (*svcr & SVCR_SM_MASK) {
save_sve_regs = true;
save_ffr = system_supports_fa64();
vl = last->sme_vl;
}
}
if (IS_ENABLED(CONFIG_ARM64_SVE) && save_sve_regs) { /* Get the configured VL from RDVL, will account for SM */ if (WARN_ON(sve_get_vl() != vl)) { /* * Can't save the user regs, so current would * re-enter user with corrupt state. * There's no way to recover, so kill it:
*/
force_signal_inject(SIGKILL, SI_KERNEL, 0, 0); return;
}
/* * All vector length selection from userspace comes through here. * We're on a slow path, so some sanity-checks are included. * If things go wrong there's a bug somewhere, but try to fall back to a * safe choice.
*/ staticunsignedint find_supported_vector_length(enum vec_type type, unsignedint vl)
{ struct vl_info *info = &vl_info[type]; int bit; int max_vl = info->max_vl;
if (WARN_ON(!sve_vl_valid(vl)))
vl = info->min_vl;
if (WARN_ON(!sve_vl_valid(max_vl)))
max_vl = info->min_vl;
if (vl > max_vl)
vl = max_vl; if (vl < info->min_vl)
vl = info->min_vl;
bit = find_next_bit(info->vq_map, SVE_VQ_MAX,
__vq_to_bit(sve_vq_from_vl(vl))); return sve_vl_from_vq(__bit_to_vq(bit));
}
for (i = 0; i < SVE_NUM_ZREGS; ++i) {
p = (__uint128_t *)ZREG(sst, vq, i);
*p = arm64_cpu_to_le128(fst->vregs[i]);
}
}
/* * Transfer the FPSIMD state in task->thread.uw.fpsimd_state to * task->thread.sve_state. * * Task can be a non-runnable task, or current. In the latter case, * the caller must have ownership of the cpu FPSIMD context before calling * this function. * task->thread.sve_state must point to at least sve_state_size(task) * bytes of allocated kernel memory. * task->thread.uw.fpsimd_state must be up to date before calling this * function.
*/ staticinlinevoid fpsimd_to_sve(struct task_struct *task)
{ unsignedint vq; void *sst = task->thread.sve_state; struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state;
if (!system_supports_sve() && !system_supports_sme()) return;
/* * Transfer the SVE state in task->thread.sve_state to * task->thread.uw.fpsimd_state. * * Task can be a non-runnable task, or current. In the latter case, * the caller must have ownership of the cpu FPSIMD context before calling * this function. * task->thread.sve_state must point to at least sve_state_size(task) * bytes of allocated kernel memory. * task->thread.sve_state must be up to date before calling this function.
*/ staticinlinevoid sve_to_fpsimd(struct task_struct *task)
{ unsignedint vq, vl; voidconst *sst = task->thread.sve_state; struct user_fpsimd_state *fst = &task->thread.uw.fpsimd_state; unsignedint i;
__uint128_t const *p;
if (!system_supports_sve() && !system_supports_sme()) return;
vl = thread_get_cur_vl(&task->thread);
vq = sve_vq_from_vl(vl); for (i = 0; i < SVE_NUM_ZREGS; ++i) {
p = (__uint128_t const *)ZREG(sst, vq, i);
fst->vregs[i] = arm64_le128_to_cpu(*p);
}
}
/* * Simulate the effects of an SMSTOP SM instruction.
*/ void task_smstop_sm(struct task_struct *task)
{ if (!thread_sm_enabled(&task->thread)) return;
__fpsimd_zero_vregs(&task->thread.uw.fpsimd_state);
task->thread.uw.fpsimd_state.fpsr = 0x0800009f; if (system_supports_fpmr())
task->thread.uw.fpmr = 0;
/* * Ensure that task->thread.sve_state is allocated and sufficiently large. * * This function should be used only in preparation for replacing * task->thread.sve_state with new data. The memory is always zeroed * here to prevent stale data from showing through: this is done in * the interest of testability and predictability: except in the * do_sve_acc() case, there is no ABI requirement to hide stale data * written previously be task.
*/ void sve_alloc(struct task_struct *task, bool flush)
{ if (task->thread.sve_state) { if (flush)
memset(task->thread.sve_state, 0,
sve_state_size(task)); return;
}
/* This is a small allocation (maximum ~8KB) and Should Not Fail. */
task->thread.sve_state =
kzalloc(sve_state_size(task), GFP_KERNEL);
}
/* * Ensure that task->thread.uw.fpsimd_state is up to date with respect to the * task's currently effective FPSIMD/SVE state. * * The task's FPSIMD/SVE/SME state must not be subject to concurrent * manipulation.
*/ void fpsimd_sync_from_effective_state(struct task_struct *task)
{ if (task->thread.fp_type == FP_STATE_SVE)
sve_to_fpsimd(task);
}
/* * Ensure that the task's currently effective FPSIMD/SVE state is up to date * with respect to task->thread.uw.fpsimd_state, zeroing any effective * non-FPSIMD (S)SVE state. * * The task's FPSIMD/SVE/SME state must not be subject to concurrent * manipulation.
*/ void fpsimd_sync_to_effective_state_zeropad(struct task_struct *task)
{ unsignedint vq; void *sst = task->thread.sve_state; struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state;
/* * Allocate the new sve_state and sme_state before freeing the old * copies so that allocation failure can be handled without needing to * mutate the task's state in any way. * * Changes to the SVE vector length must not discard live ZA state or * clear PSTATE.ZA, as userspace code which is unaware of the AAPCS64 * ZA lazy saving scheme may attempt to change the SVE vector length * while unsaved/dormant ZA state exists.
*/
sve_state = kzalloc(__sve_state_size(sve_vl, sme_vl), GFP_KERNEL); if (!sve_state) goto out_mem;
if (type == ARM64_VEC_SME) {
sme_state = kzalloc(__sme_state_size(sme_vl), GFP_KERNEL); if (!sme_state) goto out_mem;
}
if (task == current)
fpsimd_save_and_flush_current_state(); else
fpsimd_flush_task_state(task);
/* * Always preserve PSTATE.SM and the effective FPSIMD state, zeroing * other SVE state.
*/
fpsimd_sync_from_effective_state(task);
task_set_vl(task, type, vl);
kfree(task->thread.sve_state);
task->thread.sve_state = sve_state;
fpsimd_sync_to_effective_state_zeropad(task);
if (flags & ~(unsignedlong)(PR_SVE_VL_INHERIT |
PR_SVE_SET_VL_ONEXEC)) return -EINVAL;
if (!sve_vl_valid(vl)) return -EINVAL;
/* * Clamp to the maximum vector length that VL-agnostic code * can work with. A flag may be assigned in the future to * allow setting of larger vector lengths without confusing * older software.
*/ if (vl > VL_ARCH_MAX)
vl = VL_ARCH_MAX;
vl = find_supported_vector_length(type, vl);
if (!onexec && vl != task_get_vl(task, type)) { if (change_live_vector_length(task, type, vl)) return -ENOMEM;
}
if (onexec || inherit)
task_set_vl_onexec(task, type, vl); else /* Reset VL to system default on next exec: */
task_set_vl_onexec(task, type, 0);
/* * Encode the current vector length and flags for return. * This is only required for prctl(): ptrace has separate fields. * SVE and SME use the same bits for _ONEXEC and _INHERIT. * * flags are as for vec_set_vector_length().
*/ staticint vec_prctl_status(enum vec_type type, unsignedlong flags)
{ int ret;
if (flags & PR_SVE_SET_VL_ONEXEC)
ret = task_get_vl_onexec(current, type); else
ret = task_get_vl(current, type);
if (test_thread_flag(vec_vl_inherit_flag(type)))
ret |= PR_SVE_VL_INHERIT;
return ret;
}
/* PR_SVE_SET_VL */ int sve_set_current_vl(unsignedlong arg)
{ unsignedlong vl, flags; int ret;
vl = arg & PR_SVE_VL_LEN_MASK;
flags = arg & ~vl;
if (!system_supports_sve() || is_compat_task()) return -EINVAL;
ret = vec_set_vector_length(current, ARM64_VEC_SVE, vl, flags); if (ret) return ret;
return vec_prctl_status(ARM64_VEC_SVE, flags);
}
/* PR_SVE_GET_VL */ int sve_get_current_vl(void)
{ if (!system_supports_sve() || is_compat_task()) return -EINVAL;
return vec_prctl_status(ARM64_VEC_SVE, 0);
}
#ifdef CONFIG_ARM64_SME /* PR_SME_SET_VL */ int sme_set_current_vl(unsignedlong arg)
{ unsignedlong vl, flags; int ret;
vl = arg & PR_SME_VL_LEN_MASK;
flags = arg & ~vl;
if (!system_supports_sme() || is_compat_task()) return -EINVAL;
ret = vec_set_vector_length(current, ARM64_VEC_SME, vl, flags); if (ret) return ret;
return vec_prctl_status(ARM64_VEC_SME, flags);
}
/* PR_SME_GET_VL */ int sme_get_current_vl(void)
{ if (!system_supports_sme() || is_compat_task()) return -EINVAL;
/* * Initialise the set of known supported VQs for the boot CPU. * This is called during kernel boot, before secondary CPUs are brought up.
*/ void __init vec_init_vq_map(enum vec_type type)
{ struct vl_info *info = &vl_info[type];
vec_probe_vqs(info, info->vq_map);
bitmap_copy(info->vq_partial_map, info->vq_map, SVE_VQ_MAX);
}
/* * If we haven't committed to the set of supported VQs yet, filter out * those not supported by the current CPU. * This function is called during the bring-up of early secondary CPUs only.
*/ void vec_update_vq_map(enum vec_type type)
{ struct vl_info *info = &vl_info[type];
DECLARE_BITMAP(tmp_map, SVE_VQ_MAX);
/* * Check whether the current CPU supports all VQs in the committed set. * This function is called during the bring-up of late secondary CPUs only.
*/ int vec_verify_vq_map(enum vec_type type)
{ struct vl_info *info = &vl_info[type];
DECLARE_BITMAP(tmp_map, SVE_VQ_MAX); unsignedlong b;
if (!IS_ENABLED(CONFIG_KVM) || !is_hyp_mode_available()) return 0;
/* * For KVM, it is necessary to ensure that this CPU doesn't * support any vector length that guests may have probed as * unsupported.
*/
/* Recover the set of supported VQs: */
bitmap_complement(tmp_map, tmp_map, SVE_VQ_MAX); /* Find VQs supported that are not globally supported: */
bitmap_andnot(tmp_map, tmp_map, info->vq_map, SVE_VQ_MAX);
/* Find the lowest such VQ, if any: */
b = find_last_bit(tmp_map, SVE_VQ_MAX); if (b >= SVE_VQ_MAX) return 0; /* no mismatches */
/* * Mismatches above sve_max_virtualisable_vl are fine, since * no guest is allowed to configure ZCR_EL2.LEN to exceed this:
*/ if (sve_vl_from_vq(__bit_to_vq(b)) <= info->max_virtualisable_vl) {
pr_warn("%s: cpu%d: Unsupported vector length(s) present\n",
info->name, smp_processor_id()); return -EINVAL;
}
return 0;
}
staticvoid __init sve_efi_setup(void)
{ int max_vl = 0; int i;
if (!IS_ENABLED(CONFIG_EFI)) return;
for (i = 0; i < ARRAY_SIZE(vl_info); i++)
max_vl = max(vl_info[i].max_vl, max_vl);
/* * alloc_percpu() warns and prints a backtrace if this goes wrong. * This is evidence of a crippled system and we are returning void, * so no attempt is made to handle this situation here.
*/ if (!sve_vl_valid(max_vl)) goto fail;
efi_sve_state = kmalloc(SVE_SIG_REGS_SIZE(sve_vq_from_vl(max_vl)),
GFP_KERNEL); if (!efi_sve_state) goto fail;
return;
fail:
panic("Cannot allocate memory for EFI SVE save/restore");
}
/* * The SVE architecture mandates support for 128-bit vectors, * so sve_vq_map must have at least SVE_VQ_MIN set. * If something went wrong, at least try to patch it up:
*/ if (WARN_ON(!test_bit(__vq_to_bit(SVE_VQ_MIN), info->vq_map)))
set_bit(__vq_to_bit(SVE_VQ_MIN), info->vq_map);
/* * For the default VL, pick the maximum supported value <= 64. * VL == 64 is guaranteed not to grow the signal frame.
*/
set_sve_default_vl(find_supported_vector_length(ARM64_VEC_SVE, 64));
b = find_last_bit(tmp_map, SVE_VQ_MAX); if (b >= SVE_VQ_MAX) /* No non-virtualisable VLs found */
info->max_virtualisable_vl = SVE_VQ_MAX; elseif (WARN_ON(b == SVE_VQ_MAX - 1)) /* No virtualisable VLs? This is architecturally forbidden. */
info->max_virtualisable_vl = SVE_VQ_MIN; else/* b + 1 < SVE_VQ_MAX */
info->max_virtualisable_vl = sve_vl_from_vq(__bit_to_vq(b + 1));
if (info->max_virtualisable_vl > info->max_vl)
info->max_virtualisable_vl = info->max_vl;
pr_info("%s: maximum available vector length %u bytes per vector\n",
info->name, info->max_vl);
pr_info("%s: default vector length %u bytes per vector\n",
info->name, get_sve_default_vl());
/* KVM decides whether to support mismatched systems. Just warn here: */ if (sve_max_virtualisable_vl() < sve_max_vl())
pr_warn("%s: unvirtualisable vector lengths present\n",
info->name);
sve_efi_setup();
}
/* * Called from the put_task_struct() path, which cannot get here * unless dead_task is really dead and not schedulable.
*/ void fpsimd_release_task(struct task_struct *dead_task)
{
sve_free(dead_task);
sme_free(dead_task);
}
#endif/* CONFIG_ARM64_SVE */
#ifdef CONFIG_ARM64_SME
/* * Ensure that task->thread.sme_state is allocated and sufficiently large. * * This function should be used only in preparation for replacing * task->thread.sme_state with new data. The memory is always zeroed * here to prevent stale data from showing through: this is done in * the interest of testability and predictability, the architecture * guarantees that when ZA is enabled it will be zeroed.
*/ void sme_alloc(struct task_struct *task, bool flush)
{ if (task->thread.sme_state) { if (flush)
memset(task->thread.sme_state, 0,
sme_state_size(task)); return;
}
/* This could potentially be up to 64K. */
task->thread.sme_state =
kzalloc(sme_state_size(task), GFP_KERNEL);
}
void cpu_enable_sme(conststruct arm64_cpu_capabilities *__always_unused p)
{ /* Set priority for all PEs to architecturally defined minimum */
write_sysreg_s(read_sysreg_s(SYS_SMPRI_EL1) & ~SMPRI_EL1_PRIORITY_MASK,
SYS_SMPRI_EL1);
/* Allow SME in kernel */
write_sysreg(read_sysreg(CPACR_EL1) | CPACR_EL1_SMEN_EL1EN, CPACR_EL1);
isb();
/* Ensure all bits in SMCR are set to known values */
write_sysreg_s(0, SYS_SMCR_EL1);
void cpu_enable_sme2(conststruct arm64_cpu_capabilities *__always_unused p)
{ /* This must be enabled after SME */
BUILD_BUG_ON(ARM64_SME2 <= ARM64_SME);
/* Allow use of ZT0 */
write_sysreg_s(read_sysreg_s(SYS_SMCR_EL1) | SMCR_ELx_EZT0_MASK,
SYS_SMCR_EL1);
}
void cpu_enable_fa64(conststruct arm64_cpu_capabilities *__always_unused p)
{ /* This must be enabled after SME */
BUILD_BUG_ON(ARM64_SME_FA64 <= ARM64_SME);
/* Allow use of FA64 */
write_sysreg_s(read_sysreg_s(SYS_SMCR_EL1) | SMCR_ELx_FA64_MASK,
SYS_SMCR_EL1);
}
/* * SME doesn't require any particular vector length be * supported but it does require at least one. We should have * disabled the feature entirely while bringing up CPUs but * let's double check here. The bitmap is SVE_VQ_MAP sized for * sharing with SVE.
*/
WARN_ON(bitmap_empty(info->vq_map, SVE_VQ_MAX));
/* * For the default VL, pick the maximum supported value <= 32 * (256 bits) if there is one since this is guaranteed not to * grow the signal frame when in streaming mode, otherwise the * minimum available VL will be used.
*/
set_sme_default_vl(find_supported_vector_length(ARM64_VEC_SME, 32));
pr_info("SME: minimum available vector length %u bytes per vector\n",
info->min_vl);
pr_info("SME: maximum available vector length %u bytes per vector\n",
info->max_vl);
pr_info("SME: default vector length %u bytes per vector\n",
get_sme_default_vl());
}
void sme_suspend_exit(void)
{
u64 smcr = 0;
if (!system_supports_sme()) return;
if (system_supports_fa64())
smcr |= SMCR_ELx_FA64; if (system_supports_sme2())
smcr |= SMCR_ELx_EZT0;
staticvoid sve_init_regs(void)
{ /* * Convert the FPSIMD state to SVE, zeroing all the state that * is not shared with FPSIMD. If (as is likely) the current * state is live in the registers then do this there and * update our metadata for the current task including * disabling the trap, otherwise update our in-memory copy. * We are guaranteed to not be in streaming mode, we can only * take a SVE trap when not in streaming mode and we can't be * in streaming mode when taking a SME trap.
*/ if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) { unsignedlong vq_minus_one =
sve_vq_from_vl(task_get_sve_vl(current)) - 1;
sve_set_vq(vq_minus_one);
sve_flush_live(true, vq_minus_one);
fpsimd_bind_task_to_cpu();
} else {
fpsimd_to_sve(current);
current->thread.fp_type = FP_STATE_SVE;
fpsimd_flush_task_state(current);
}
}
/* * Trapped SVE access * * Storage is allocated for the full SVE state, the current FPSIMD * register contents are migrated across, and the access trap is * disabled. * * TIF_SVE should be clear on entry: otherwise, fpsimd_restore_current_state() * would have disabled the SVE access trap for userspace during * ret_to_user, making an SVE access trap impossible in that case.
*/ void do_sve_acc(unsignedlong esr, struct pt_regs *regs)
{ /* Even if we chose not to use SVE, the hardware could still trap: */ if (unlikely(!system_supports_sve()) || WARN_ON(is_compat_task())) {
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0); return;
}
sve_alloc(current, true); if (!current->thread.sve_state) {
force_sig(SIGKILL); return;
}
get_cpu_fpsimd_context();
if (test_and_set_thread_flag(TIF_SVE))
WARN_ON(1); /* SVE access shouldn't have trapped */
/* * Even if the task can have used streaming mode we can only * generate SVE access traps in normal SVE mode and * transitioning out of streaming mode may discard any * streaming mode state. Always clear the high bits to avoid * any potential errors tracking what is properly initialised.
*/
sve_init_regs();
put_cpu_fpsimd_context();
}
/* * Trapped SME access * * Storage is allocated for the full SVE and SME state, the current * FPSIMD register contents are migrated to SVE if SVE is not already * active, and the access trap is disabled. * * TIF_SME should be clear on entry: otherwise, fpsimd_restore_current_state() * would have disabled the SME access trap for userspace during * ret_to_user, making an SME access trap impossible in that case.
*/ void do_sme_acc(unsignedlong esr, struct pt_regs *regs)
{ /* Even if we chose not to use SME, the hardware could still trap: */ if (unlikely(!system_supports_sme()) || WARN_ON(is_compat_task())) {
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0); return;
}
/* * If this not a trap due to SME being disabled then something * is being used in the wrong mode, report as SIGILL.
*/ if (ESR_ELx_SME_ISS_SMTC(esr) != ESR_ELx_SME_ISS_SMTC_SME_DISABLED) {
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0); return;
}
/* * Trapped FP/ASIMD access.
*/ void do_fpsimd_acc(unsignedlong esr, struct pt_regs *regs)
{ /* Even if we chose not to use FPSIMD, the hardware could still trap: */ if (!system_supports_fpsimd()) {
force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0); return;
}
/* * When FPSIMD is enabled, we should never take a trap unless something * has gone very wrong.
*/
BUG();
}
/* * Raise a SIGFPE for the current process.
*/ void do_fpsimd_exc(unsignedlong esr, struct pt_regs *regs)
{ unsignedint si_code = FPE_FLTUNK;
/* * Elide the load if this CPU holds the most recent kernel mode * FPSIMD context of the current task.
*/ if (last->st == &task->thread.kernel_fpsimd_state &&
task->thread.kernel_fpsimd_cpu == smp_processor_id()) return;
/* * Invalidate any task's FPSIMD state that is present on this cpu. * The FPSIMD context should be acquired with get_cpu_fpsimd_context() * before calling this function.
*/ staticvoid fpsimd_flush_cpu_state(void)
{
WARN_ON(!system_supports_fpsimd());
__this_cpu_write(fpsimd_last_state.st, NULL);
/* * Leaving streaming mode enabled will cause issues for any kernel * NEON and leaving streaming mode or ZA enabled may increase power * consumption.
*/ if (system_supports_sme())
sme_smstop();
/* Save unsaved fpsimd state, if any: */ if (test_thread_flag(TIF_KERNEL_FPSTATE))
fpsimd_save_kernel_state(current); else
fpsimd_save_user_state();
if (test_tsk_thread_flag(next, TIF_KERNEL_FPSTATE)) {
fpsimd_flush_cpu_state();
fpsimd_load_kernel_state(next);
} else { /* * Fix up TIF_FOREIGN_FPSTATE to correctly describe next's * state. For kernel threads, FPSIMD registers are never * loaded with user mode FPSIMD state and so wrong_task and * wrong_cpu will always be true.
*/
wrong_task = __this_cpu_read(fpsimd_last_state.st) !=
&next->thread.uw.fpsimd_state;
wrong_cpu = next->thread.fpsimd_cpu != smp_processor_id();
staticvoid fpsimd_flush_thread_vl(enum vec_type type)
{ int vl, supported_vl;
/* * Reset the task vector length as required. This is where we * ensure that all user tasks have a valid vector length * configured: no kernel task can become a user task without * an exec and hence a call to this function. By the time the * first call to this function is made, all early hardware * probing is complete, so __sve_default_vl should be valid. * If a bug causes this to go wrong, we make some noise and * try to fudge thread.sve_vl to a safe value here.
*/
vl = task_get_vl_onexec(current, type); if (!vl)
vl = get_default_vl(type);
if (WARN_ON(!sve_vl_valid(vl)))
vl = vl_info[type].min_vl;
/* * If the task is not set to inherit, ensure that the vector * length will be reset by a subsequent exec:
*/ if (!test_thread_flag(vec_vl_inherit_flag(type)))
task_set_vl_onexec(current, type, 0);
}
/* * Save the userland FPSIMD state of 'current' to memory, but only if the state * currently held in the registers does in fact belong to 'current'
*/ void fpsimd_preserve_current_state(void)
{ if (!system_supports_fpsimd()) return;
/* * Associate current's FPSIMD context with this cpu * The caller must have ownership of the cpu FPSIMD context before calling * this function.
*/ staticvoid fpsimd_bind_task_to_cpu(void)
{ struct cpu_fp_state *last = this_cpu_ptr(&fpsimd_last_state);
/* * Toggle SVE and SME trapping for userspace if needed, these * are serialsied by ret_to_user().
*/ if (system_supports_sme()) { if (test_thread_flag(TIF_SME))
sme_user_enable(); else
sme_user_disable();
}
if (system_supports_sve()) { if (test_thread_flag(TIF_SVE))
sve_user_enable(); else
sve_user_disable();
}
}
/* * Load the userland FPSIMD state of 'current' from memory, but only if the * FPSIMD state already held in the registers is /not/ the most recent FPSIMD * state of 'current'. This is called when we are preparing to return to * userspace to ensure that userspace sees a good register state.
*/ void fpsimd_restore_current_state(void)
{ /* * TIF_FOREIGN_FPSTATE is set on the init task and copied by * arch_dup_task_struct() regardless of whether FP/SIMD is detected. * Thus user threads can have this set even when FP/SIMD hasn't been * detected. * * When FP/SIMD is detected, begin_new_exec() will set * TIF_FOREIGN_FPSTATE via flush_thread() -> fpsimd_flush_thread(), * and fpsimd_thread_switch() will set TIF_FOREIGN_FPSTATE when * switching tasks. We detect FP/SIMD before we exec the first user * process, ensuring this has TIF_FOREIGN_FPSTATE set and * do_notify_resume() will call fpsimd_restore_current_state() to * install the user FP/SIMD context. * * When FP/SIMD is not detected, nothing else will clear or set * TIF_FOREIGN_FPSTATE prior to the first return to userspace, and * we must clear TIF_FOREIGN_FPSTATE to avoid do_notify_resume() * looping forever calling fpsimd_restore_current_state().
*/ if (!system_supports_fpsimd()) {
clear_thread_flag(TIF_FOREIGN_FPSTATE); return;
}
get_cpu_fpsimd_context();
if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) {
task_fpsimd_load();
fpsimd_bind_task_to_cpu();
}
put_cpu_fpsimd_context();
}
void fpsimd_update_current_state(struct user_fpsimd_state const *state)
{ if (WARN_ON(!system_supports_fpsimd())) return;
current->thread.uw.fpsimd_state = *state; if (current->thread.fp_type == FP_STATE_SVE)
fpsimd_to_sve(current);
}
/* * Invalidate live CPU copies of task t's FPSIMD state * * This function may be called with preemption enabled. The barrier() * ensures that the assignment to fpsimd_cpu is visible to any * preemption/softirq that could race with set_tsk_thread_flag(), so * that TIF_FOREIGN_FPSTATE cannot be spuriously re-cleared. * * The final barrier ensures that TIF_FOREIGN_FPSTATE is seen set by any * subsequent code.
*/ void fpsimd_flush_task_state(struct task_struct *t)
{
t->thread.fpsimd_cpu = NR_CPUS; /* * If we don't support fpsimd, bail out after we have * reset the fpsimd_cpu for this task and clear the * FPSTATE.
*/ if (!system_supports_fpsimd()) return;
barrier();
set_tsk_thread_flag(t, TIF_FOREIGN_FPSTATE);
barrier();
}
void fpsimd_save_and_flush_current_state(void)
{ if (!system_supports_fpsimd()) return;
/* * Save the FPSIMD state to memory and invalidate cpu view. * This function must be called with preemption disabled.
*/ void fpsimd_save_and_flush_cpu_state(void)
{ unsignedlong flags;
if (!system_supports_fpsimd()) return;
WARN_ON(preemptible());
local_irq_save(flags);
fpsimd_save_user_state();
fpsimd_flush_cpu_state();
local_irq_restore(flags);
}
#ifdef CONFIG_KERNEL_MODE_NEON
/* * Kernel-side NEON support functions
*/
/* * kernel_neon_begin(): obtain the CPU FPSIMD registers for use by the calling * context * * Must not be called unless may_use_simd() returns true. * Task context in the FPSIMD registers is saved back to memory as necessary. * * A matching call to kernel_neon_end() must be made before returning from the * calling context. * * The caller may freely use the FPSIMD registers until kernel_neon_end() is * called.
*/ void kernel_neon_begin(void)
{ if (WARN_ON(!system_supports_fpsimd())) return;
BUG_ON(!may_use_simd());
get_cpu_fpsimd_context();
/* Save unsaved fpsimd state, if any: */ if (test_thread_flag(TIF_KERNEL_FPSTATE)) {
BUG_ON(IS_ENABLED(CONFIG_PREEMPT_RT) || !in_serving_softirq());
fpsimd_save_kernel_state(current);
} else {
fpsimd_save_user_state();
/* * Set the thread flag so that the kernel mode FPSIMD state * will be context switched along with the rest of the task * state. * * On non-PREEMPT_RT, softirqs may interrupt task level kernel * mode FPSIMD, but the task will not be preemptible so setting * TIF_KERNEL_FPSTATE for those would be both wrong (as it * would mark the task context FPSIMD state as requiring a * context switch) and unnecessary. * * On PREEMPT_RT, softirqs are serviced from a separate thread, * which is scheduled as usual, and this guarantees that these * softirqs are not interrupting use of the FPSIMD in kernel * mode in task context. So in this case, setting the flag here * is always appropriate.
*/ if (IS_ENABLED(CONFIG_PREEMPT_RT) || !in_serving_softirq())
set_thread_flag(TIF_KERNEL_FPSTATE);
}
/* Invalidate any task state remaining in the fpsimd regs: */
fpsimd_flush_cpu_state();
/* * kernel_neon_end(): give the CPU FPSIMD registers back to the current task * * Must be called from a context in which kernel_neon_begin() was previously * called, with no call to kernel_neon_end() in the meantime. * * The caller must not use the FPSIMD registers after this function is called, * unless kernel_neon_begin() is called again in the meantime.
*/ void kernel_neon_end(void)
{ if (!system_supports_fpsimd()) return;
/* * If we are returning from a nested use of kernel mode FPSIMD, restore * the task context kernel mode FPSIMD state. This can only happen when * running in softirq context on non-PREEMPT_RT.
*/ if (!IS_ENABLED(CONFIG_PREEMPT_RT) && in_serving_softirq() &&
test_thread_flag(TIF_KERNEL_FPSTATE))
fpsimd_load_kernel_state(current); else
clear_thread_flag(TIF_KERNEL_FPSTATE);
}
EXPORT_SYMBOL_GPL(kernel_neon_end);
/* * EFI runtime services support functions * * The ABI for EFI runtime services allows EFI to use FPSIMD during the call. * This means that for EFI (and only for EFI), we have to assume that FPSIMD * is always used rather than being an optional accelerator. * * These functions provide the necessary support for ensuring FPSIMD * save/restore in the contexts from which EFI is used. * * Do not use them for any other purpose -- if tempted to do so, you are * either doing something wrong or you need to propose some refactoring.
*/
/* * __efi_fpsimd_begin(): prepare FPSIMD for making an EFI runtime services call
*/ void __efi_fpsimd_begin(void)
{ if (!system_supports_fpsimd()) return;
WARN_ON(preemptible());
if (may_use_simd()) {
kernel_neon_begin();
} else { /* * If !efi_sve_state, SVE can't be in use yet and doesn't need * preserving:
*/ if (system_supports_sve() && efi_sve_state != NULL) { bool ffr = true;
u64 svcr;
efi_sve_state_used = true;
if (system_supports_sme()) {
svcr = read_sysreg_s(SYS_SVCR);
efi_sm_state = svcr & SVCR_SM_MASK;
/* * Unless we have FA64 FFR does not * exist in streaming mode.
*/ if (!system_supports_fa64())
ffr = !(svcr & SVCR_SM_MASK);
}
if (system_supports_sme())
sysreg_clear_set_s(SYS_SVCR,
SVCR_SM_MASK, 0);
} else {
fpsimd_save_state(&efi_fpsimd_state);
}
efi_fpsimd_state_used = true;
}
}
/* * __efi_fpsimd_end(): clean up FPSIMD after an EFI runtime services call
*/ void __efi_fpsimd_end(void)
{ if (!system_supports_fpsimd()) return;
if (!efi_fpsimd_state_used) {
kernel_neon_end();
} else { if (system_supports_sve() && efi_sve_state_used) { bool ffr = true;
/* * Restore streaming mode; EFI calls are * normal function calls so should not return in * streaming mode.
*/ if (system_supports_sme()) { if (efi_sm_state) {
sysreg_clear_set_s(SYS_SVCR,
0,
SVCR_SM_MASK);
/* * Unless we have FA64 FFR does not * exist in streaming mode.
*/ if (!system_supports_fa64())
ffr = false;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.