1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Copyright (C) 2012 Regents of the University of California 4 */ 5 6 #ifndef _ASM_RISCV_SWITCH_TO_H 7 #define _ASM_RISCV_SWITCH_TO_H 8 9 #include <linux/jump_label.h> 10 #include <linux/sched/task_stack.h> 11 #include <linux/mm_types.h> 12 #include <asm/vector.h> 13 #include <asm/cpufeature.h> 14 #include <asm/processor.h> 15 #include <asm/ptrace.h> 16 #include <asm/csr.h> 17 #include <asm/qos.h> 18 19 #ifdef CONFIG_FPU 20 extern void __fstate_save(struct task_struct *save_to); 21 extern void __fstate_restore(struct task_struct *restore_from); 22 23 static inline void __fstate_clean(struct pt_regs *regs) 24 { 25 regs->status = (regs->status & ~SR_FS) | SR_FS_CLEAN; 26 } 27 28 static inline void fstate_off(struct task_struct *task, 29 struct pt_regs *regs) 30 { 31 regs->status = (regs->status & ~SR_FS) | SR_FS_OFF; 32 } 33 34 static inline void fstate_save(struct task_struct *task, 35 struct pt_regs *regs) 36 { 37 if ((regs->status & SR_FS) == SR_FS_DIRTY) { 38 __fstate_save(task); 39 __fstate_clean(regs); 40 } 41 } 42 43 static inline void fstate_restore(struct task_struct *task, 44 struct pt_regs *regs) 45 { 46 if ((regs->status & SR_FS) != SR_FS_OFF) { 47 __fstate_restore(task); 48 __fstate_clean(regs); 49 } 50 } 51 52 static inline void __switch_to_fpu(struct task_struct *prev, 53 struct task_struct *next) 54 { 55 struct pt_regs *regs; 56 57 regs = task_pt_regs(prev); 58 fstate_save(prev, regs); 59 fstate_restore(next, task_pt_regs(next)); 60 } 61 62 static __always_inline bool has_fpu(void) 63 { 64 return riscv_has_extension_likely(RISCV_ISA_EXT_F) || 65 riscv_has_extension_likely(RISCV_ISA_EXT_D); 66 } 67 #else 68 static __always_inline bool has_fpu(void) { return false; } 69 #define fstate_save(task, regs) do { } while (0) 70 #define fstate_restore(task, regs) do { } while (0) 71 #define __switch_to_fpu(__prev, __next) do { } while (0) 72 #endif 73 74 static inline void envcfg_update_bits(struct task_struct *task, 75 unsigned long mask, unsigned long val) 76 { 77 unsigned long envcfg; 78 79 envcfg = (task->thread.envcfg & ~mask) | val; 80 task->thread.envcfg = envcfg; 81 if (task == current) 82 csr_write(CSR_ENVCFG, envcfg); 83 } 84 85 static inline void __switch_to_envcfg(struct task_struct *next) 86 { 87 asm volatile (ALTERNATIVE("nop", "csrw " __stringify(CSR_ENVCFG) ", %0", 88 0, RISCV_ISA_EXT_XLINUXENVCFG, 1) 89 :: "r" (next->thread.envcfg) : "memory"); 90 } 91 92 extern struct task_struct *__switch_to(struct task_struct *, 93 struct task_struct *); 94 95 static inline bool switch_to_should_flush_icache(struct task_struct *task) 96 { 97 #ifdef CONFIG_SMP 98 bool stale_mm = task->mm && task->mm->context.force_icache_flush; 99 bool stale_thread = task->thread.force_icache_flush; 100 bool thread_migrated = smp_processor_id() != task->thread.prev_cpu; 101 102 return thread_migrated && (stale_mm || stale_thread); 103 #else 104 return false; 105 #endif 106 } 107 108 #ifdef CONFIG_SMP 109 #define __set_prev_cpu(thread) ((thread).prev_cpu = smp_processor_id()) 110 #else 111 #define __set_prev_cpu(thread) 112 #endif 113 114 #define switch_to(prev, next, last) \ 115 do { \ 116 struct task_struct *__prev = (prev); \ 117 struct task_struct *__next = (next); \ 118 __set_prev_cpu(__prev->thread); \ 119 if (has_fpu()) \ 120 __switch_to_fpu(__prev, __next); \ 121 if (has_vector() || has_xtheadvector()) \ 122 __switch_to_vector(__prev, __next); \ 123 if (has_srmcfg()) \ 124 __switch_to_srmcfg(__next); \ 125 if (switch_to_should_flush_icache(__next)) \ 126 local_flush_icache_all(); \ 127 __switch_to_envcfg(__next); \ 128 ((last) = __switch_to(__prev, __next)); \ 129 } while (0) 130 131 #endif /* _ASM_RISCV_SWITCH_TO_H */ 132