cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fpu.h (7332B)


      1/* SPDX-License-Identifier: GPL-2.0-or-later */
      2/*
      3 * Copyright (C) 2002 MontaVista Software Inc.
      4 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
      5 */
      6#ifndef _ASM_FPU_H
      7#define _ASM_FPU_H
      8
      9#include <linux/sched.h>
     10#include <linux/sched/task_stack.h>
     11#include <linux/ptrace.h>
     12#include <linux/thread_info.h>
     13#include <linux/bitops.h>
     14
     15#include <asm/mipsregs.h>
     16#include <asm/cpu.h>
     17#include <asm/cpu-features.h>
     18#include <asm/fpu_emulator.h>
     19#include <asm/hazards.h>
     20#include <asm/ptrace.h>
     21#include <asm/processor.h>
     22#include <asm/current.h>
     23#include <asm/msa.h>
     24
     25#ifdef CONFIG_MIPS_MT_FPAFF
     26#include <asm/mips_mt.h>
     27#endif
     28
     29/*
     30 * This enum specifies a mode in which we want the FPU to operate, for cores
     31 * which implement the Status.FR bit. Note that the bottom bit of the value
     32 * purposefully matches the desired value of the Status.FR bit.
     33 */
     34enum fpu_mode {
     35	FPU_32BIT = 0,		/* FR = 0 */
     36	FPU_64BIT,		/* FR = 1, FRE = 0 */
     37	FPU_AS_IS,
     38	FPU_HYBRID,		/* FR = 1, FRE = 1 */
     39
     40#define FPU_FR_MASK		0x1
     41};
     42
     43#ifdef CONFIG_MIPS_FP_SUPPORT
     44
     45extern void _save_fp(struct task_struct *);
     46extern void _restore_fp(struct task_struct *);
     47
     48#define __disable_fpu()							\
     49do {									\
     50	clear_c0_status(ST0_CU1);					\
     51	disable_fpu_hazard();						\
     52} while (0)
     53
     54static inline int __enable_fpu(enum fpu_mode mode)
     55{
     56	int fr;
     57
     58	switch (mode) {
     59	case FPU_AS_IS:
     60		/* just enable the FPU in its current mode */
     61		set_c0_status(ST0_CU1);
     62		enable_fpu_hazard();
     63		return 0;
     64
     65	case FPU_HYBRID:
     66		if (!cpu_has_fre)
     67			return SIGFPE;
     68
     69		/* set FRE */
     70		set_c0_config5(MIPS_CONF5_FRE);
     71		goto fr_common;
     72
     73	case FPU_64BIT:
     74#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR5) || \
     75      defined(CONFIG_CPU_MIPSR6) || defined(CONFIG_64BIT))
     76		/* we only have a 32-bit FPU */
     77		return SIGFPE;
     78#endif
     79		/* fallthrough */
     80	case FPU_32BIT:
     81		if (cpu_has_fre) {
     82			/* clear FRE */
     83			clear_c0_config5(MIPS_CONF5_FRE);
     84		}
     85fr_common:
     86		/* set CU1 & change FR appropriately */
     87		fr = (int)mode & FPU_FR_MASK;
     88		change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
     89		enable_fpu_hazard();
     90
     91		/* check FR has the desired value */
     92		if (!!(read_c0_status() & ST0_FR) == !!fr)
     93			return 0;
     94
     95		/* unsupported FR value */
     96		__disable_fpu();
     97		return SIGFPE;
     98
     99	default:
    100		BUG();
    101	}
    102
    103	return SIGFPE;
    104}
    105
    106#define clear_fpu_owner()	clear_thread_flag(TIF_USEDFPU)
    107
    108static inline int __is_fpu_owner(void)
    109{
    110	return test_thread_flag(TIF_USEDFPU);
    111}
    112
    113static inline int is_fpu_owner(void)
    114{
    115	return cpu_has_fpu && __is_fpu_owner();
    116}
    117
    118static inline int __own_fpu(void)
    119{
    120	enum fpu_mode mode;
    121	int ret;
    122
    123	if (test_thread_flag(TIF_HYBRID_FPREGS))
    124		mode = FPU_HYBRID;
    125	else
    126		mode = !test_thread_flag(TIF_32BIT_FPREGS);
    127
    128	ret = __enable_fpu(mode);
    129	if (ret)
    130		return ret;
    131
    132	KSTK_STATUS(current) |= ST0_CU1;
    133	if (mode == FPU_64BIT || mode == FPU_HYBRID)
    134		KSTK_STATUS(current) |= ST0_FR;
    135	else /* mode == FPU_32BIT */
    136		KSTK_STATUS(current) &= ~ST0_FR;
    137
    138	set_thread_flag(TIF_USEDFPU);
    139	return 0;
    140}
    141
    142static inline int own_fpu_inatomic(int restore)
    143{
    144	int ret = 0;
    145
    146	if (cpu_has_fpu && !__is_fpu_owner()) {
    147		ret = __own_fpu();
    148		if (restore && !ret)
    149			_restore_fp(current);
    150	}
    151	return ret;
    152}
    153
    154static inline int own_fpu(int restore)
    155{
    156	int ret;
    157
    158	preempt_disable();
    159	ret = own_fpu_inatomic(restore);
    160	preempt_enable();
    161	return ret;
    162}
    163
    164static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
    165{
    166	if (is_msa_enabled()) {
    167		if (save) {
    168			save_msa(tsk);
    169			tsk->thread.fpu.fcr31 =
    170					read_32bit_cp1_register(CP1_STATUS);
    171		}
    172		disable_msa();
    173		clear_tsk_thread_flag(tsk, TIF_USEDMSA);
    174		__disable_fpu();
    175	} else if (is_fpu_owner()) {
    176		if (save)
    177			_save_fp(tsk);
    178		__disable_fpu();
    179	} else {
    180		/* FPU should not have been left enabled with no owner */
    181		WARN(read_c0_status() & ST0_CU1,
    182		     "Orphaned FPU left enabled");
    183	}
    184	KSTK_STATUS(tsk) &= ~ST0_CU1;
    185	clear_tsk_thread_flag(tsk, TIF_USEDFPU);
    186}
    187
    188static inline void lose_fpu(int save)
    189{
    190	preempt_disable();
    191	lose_fpu_inatomic(save, current);
    192	preempt_enable();
    193}
    194
    195/**
    196 * init_fp_ctx() - Initialize task FP context
    197 * @target: The task whose FP context should be initialized.
    198 *
    199 * Initializes the FP context of the target task to sane default values if that
    200 * target task does not already have valid FP context. Once the context has
    201 * been initialized, the task will be marked as having used FP & thus having
    202 * valid FP context.
    203 *
    204 * Returns: true if context is initialized, else false.
    205 */
    206static inline bool init_fp_ctx(struct task_struct *target)
    207{
    208	/* If FP has been used then the target already has context */
    209	if (tsk_used_math(target))
    210		return false;
    211
    212	/* Begin with data registers set to all 1s... */
    213	memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
    214
    215	/* FCSR has been preset by `mips_set_personality_nan'.  */
    216
    217	/*
    218	 * Record that the target has "used" math, such that the context
    219	 * just initialised, and any modifications made by the caller,
    220	 * aren't discarded.
    221	 */
    222	set_stopped_child_used_math(target);
    223
    224	return true;
    225}
    226
    227static inline void save_fp(struct task_struct *tsk)
    228{
    229	if (cpu_has_fpu)
    230		_save_fp(tsk);
    231}
    232
    233static inline void restore_fp(struct task_struct *tsk)
    234{
    235	if (cpu_has_fpu)
    236		_restore_fp(tsk);
    237}
    238
    239static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
    240{
    241	if (tsk == current) {
    242		preempt_disable();
    243		if (is_fpu_owner())
    244			_save_fp(current);
    245		preempt_enable();
    246	}
    247
    248	return tsk->thread.fpu.fpr;
    249}
    250
    251#else /* !CONFIG_MIPS_FP_SUPPORT */
    252
    253/*
    254 * When FP support is disabled we provide only a minimal set of stub functions
    255 * to avoid callers needing to care too much about CONFIG_MIPS_FP_SUPPORT.
    256 */
    257
    258static inline int __enable_fpu(enum fpu_mode mode)
    259{
    260	return SIGILL;
    261}
    262
    263static inline void __disable_fpu(void)
    264{
    265	/* no-op */
    266}
    267
    268
    269static inline int is_fpu_owner(void)
    270{
    271	return 0;
    272}
    273
    274static inline void clear_fpu_owner(void)
    275{
    276	/* no-op */
    277}
    278
    279static inline int own_fpu_inatomic(int restore)
    280{
    281	return SIGILL;
    282}
    283
    284static inline int own_fpu(int restore)
    285{
    286	return SIGILL;
    287}
    288
    289static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
    290{
    291	/* no-op */
    292}
    293
    294static inline void lose_fpu(int save)
    295{
    296	/* no-op */
    297}
    298
    299static inline bool init_fp_ctx(struct task_struct *target)
    300{
    301	return false;
    302}
    303
    304/*
    305 * The following functions should only be called in paths where we know that FP
    306 * support is enabled, typically a path where own_fpu() or __enable_fpu() have
    307 * returned successfully. When CONFIG_MIPS_FP_SUPPORT=n it is known at compile
    308 * time that this should never happen, so calls to these functions should be
    309 * optimized away & never actually be emitted.
    310 */
    311
    312extern void save_fp(struct task_struct *tsk)
    313	__compiletime_error("save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
    314
    315extern void _save_fp(struct task_struct *)
    316	__compiletime_error("_save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
    317
    318extern void restore_fp(struct task_struct *tsk)
    319	__compiletime_error("restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
    320
    321extern void _restore_fp(struct task_struct *)
    322	__compiletime_error("_restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
    323
    324extern union fpureg *get_fpu_regs(struct task_struct *tsk)
    325	__compiletime_error("get_fpu_regs() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
    326
    327#endif /* !CONFIG_MIPS_FP_SUPPORT */
    328#endif /* _ASM_FPU_H */