1dc6667a4SGuo Ren /* SPDX-License-Identifier: GPL-2.0-or-later */
2dc6667a4SGuo Ren /*
3dc6667a4SGuo Ren * Copyright (C) 2020 SiFive
4dc6667a4SGuo Ren */
5dc6667a4SGuo Ren
6dc6667a4SGuo Ren #ifndef __ASM_RISCV_VECTOR_H
7dc6667a4SGuo Ren #define __ASM_RISCV_VECTOR_H
8dc6667a4SGuo Ren
9dc6667a4SGuo Ren #include <linux/types.h>
107017858eSGreentime Hu #include <uapi/asm-generic/errno.h>
11dc6667a4SGuo Ren
12dc6667a4SGuo Ren #ifdef CONFIG_RISCV_ISA_V
13dc6667a4SGuo Ren
1403c3fcd9SGreentime Hu #include <linux/stringify.h>
153a2df632SGreentime Hu #include <linux/sched.h>
163a2df632SGreentime Hu #include <linux/sched/task_stack.h>
173a2df632SGreentime Hu #include <asm/ptrace.h>
18dc6667a4SGuo Ren #include <asm/hwcap.h>
190a3381a0SGreentime Hu #include <asm/csr.h>
2003c3fcd9SGreentime Hu #include <asm/asm.h>
21dc6667a4SGuo Ren
227017858eSGreentime Hu extern unsigned long riscv_v_vsize;
237017858eSGreentime Hu int riscv_v_setup_vsize(void);
24cd054837SAndy Chiu bool riscv_v_first_use_handler(struct pt_regs *regs);
257017858eSGreentime Hu
has_vector(void)26dc6667a4SGuo Ren static __always_inline bool has_vector(void)
27dc6667a4SGuo Ren {
28dc6667a4SGuo Ren return riscv_has_extension_unlikely(RISCV_ISA_EXT_v);
29dc6667a4SGuo Ren }
30dc6667a4SGuo Ren
__riscv_v_vstate_clean(struct pt_regs * regs)3103c3fcd9SGreentime Hu static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
3203c3fcd9SGreentime Hu {
3303c3fcd9SGreentime Hu regs->status = (regs->status & ~SR_VS) | SR_VS_CLEAN;
3403c3fcd9SGreentime Hu }
3503c3fcd9SGreentime Hu
__riscv_v_vstate_dirty(struct pt_regs * regs)369657e9b7SBjörn Töpel static inline void __riscv_v_vstate_dirty(struct pt_regs *regs)
379657e9b7SBjörn Töpel {
389657e9b7SBjörn Töpel regs->status = (regs->status & ~SR_VS) | SR_VS_DIRTY;
399657e9b7SBjörn Töpel }
409657e9b7SBjörn Töpel
riscv_v_vstate_off(struct pt_regs * regs)4103c3fcd9SGreentime Hu static inline void riscv_v_vstate_off(struct pt_regs *regs)
4203c3fcd9SGreentime Hu {
4303c3fcd9SGreentime Hu regs->status = (regs->status & ~SR_VS) | SR_VS_OFF;
4403c3fcd9SGreentime Hu }
4503c3fcd9SGreentime Hu
riscv_v_vstate_on(struct pt_regs * regs)4603c3fcd9SGreentime Hu static inline void riscv_v_vstate_on(struct pt_regs *regs)
4703c3fcd9SGreentime Hu {
4803c3fcd9SGreentime Hu regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL;
4903c3fcd9SGreentime Hu }
5003c3fcd9SGreentime Hu
riscv_v_vstate_query(struct pt_regs * regs)5103c3fcd9SGreentime Hu static inline bool riscv_v_vstate_query(struct pt_regs *regs)
5203c3fcd9SGreentime Hu {
5303c3fcd9SGreentime Hu return (regs->status & SR_VS) != 0;
5403c3fcd9SGreentime Hu }
5503c3fcd9SGreentime Hu
riscv_v_enable(void)560a3381a0SGreentime Hu static __always_inline void riscv_v_enable(void)
570a3381a0SGreentime Hu {
580a3381a0SGreentime Hu csr_set(CSR_SSTATUS, SR_VS);
590a3381a0SGreentime Hu }
600a3381a0SGreentime Hu
riscv_v_disable(void)610a3381a0SGreentime Hu static __always_inline void riscv_v_disable(void)
620a3381a0SGreentime Hu {
630a3381a0SGreentime Hu csr_clear(CSR_SSTATUS, SR_VS);
640a3381a0SGreentime Hu }
650a3381a0SGreentime Hu
__vstate_csr_save(struct __riscv_v_ext_state * dest)6603c3fcd9SGreentime Hu static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
6703c3fcd9SGreentime Hu {
6803c3fcd9SGreentime Hu asm volatile (
6903c3fcd9SGreentime Hu "csrr %0, " __stringify(CSR_VSTART) "\n\t"
7003c3fcd9SGreentime Hu "csrr %1, " __stringify(CSR_VTYPE) "\n\t"
7103c3fcd9SGreentime Hu "csrr %2, " __stringify(CSR_VL) "\n\t"
7203c3fcd9SGreentime Hu "csrr %3, " __stringify(CSR_VCSR) "\n\t"
73*c35f3aa3SAndy Chiu "csrr %4, " __stringify(CSR_VLENB) "\n\t"
7403c3fcd9SGreentime Hu : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
75*c35f3aa3SAndy Chiu "=r" (dest->vcsr), "=r" (dest->vlenb) : :);
7603c3fcd9SGreentime Hu }
7703c3fcd9SGreentime Hu
__vstate_csr_restore(struct __riscv_v_ext_state * src)7803c3fcd9SGreentime Hu static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src)
7903c3fcd9SGreentime Hu {
8003c3fcd9SGreentime Hu asm volatile (
8103c3fcd9SGreentime Hu ".option push\n\t"
8203c3fcd9SGreentime Hu ".option arch, +v\n\t"
8303c3fcd9SGreentime Hu "vsetvl x0, %2, %1\n\t"
8403c3fcd9SGreentime Hu ".option pop\n\t"
8503c3fcd9SGreentime Hu "csrw " __stringify(CSR_VSTART) ", %0\n\t"
8603c3fcd9SGreentime Hu "csrw " __stringify(CSR_VCSR) ", %3\n\t"
8703c3fcd9SGreentime Hu : : "r" (src->vstart), "r" (src->vtype), "r" (src->vl),
8803c3fcd9SGreentime Hu "r" (src->vcsr) :);
8903c3fcd9SGreentime Hu }
9003c3fcd9SGreentime Hu
__riscv_v_vstate_save(struct __riscv_v_ext_state * save_to,void * datap)9103c3fcd9SGreentime Hu static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
9203c3fcd9SGreentime Hu void *datap)
9303c3fcd9SGreentime Hu {
9403c3fcd9SGreentime Hu unsigned long vl;
9503c3fcd9SGreentime Hu
9603c3fcd9SGreentime Hu riscv_v_enable();
9703c3fcd9SGreentime Hu __vstate_csr_save(save_to);
9803c3fcd9SGreentime Hu asm volatile (
9903c3fcd9SGreentime Hu ".option push\n\t"
10003c3fcd9SGreentime Hu ".option arch, +v\n\t"
10103c3fcd9SGreentime Hu "vsetvli %0, x0, e8, m8, ta, ma\n\t"
10203c3fcd9SGreentime Hu "vse8.v v0, (%1)\n\t"
10303c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
10403c3fcd9SGreentime Hu "vse8.v v8, (%1)\n\t"
10503c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
10603c3fcd9SGreentime Hu "vse8.v v16, (%1)\n\t"
10703c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
10803c3fcd9SGreentime Hu "vse8.v v24, (%1)\n\t"
10903c3fcd9SGreentime Hu ".option pop\n\t"
11003c3fcd9SGreentime Hu : "=&r" (vl) : "r" (datap) : "memory");
11103c3fcd9SGreentime Hu riscv_v_disable();
11203c3fcd9SGreentime Hu }
11303c3fcd9SGreentime Hu
__riscv_v_vstate_restore(struct __riscv_v_ext_state * restore_from,void * datap)11403c3fcd9SGreentime Hu static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
11503c3fcd9SGreentime Hu void *datap)
11603c3fcd9SGreentime Hu {
11703c3fcd9SGreentime Hu unsigned long vl;
11803c3fcd9SGreentime Hu
11903c3fcd9SGreentime Hu riscv_v_enable();
12003c3fcd9SGreentime Hu asm volatile (
12103c3fcd9SGreentime Hu ".option push\n\t"
12203c3fcd9SGreentime Hu ".option arch, +v\n\t"
12303c3fcd9SGreentime Hu "vsetvli %0, x0, e8, m8, ta, ma\n\t"
12403c3fcd9SGreentime Hu "vle8.v v0, (%1)\n\t"
12503c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
12603c3fcd9SGreentime Hu "vle8.v v8, (%1)\n\t"
12703c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
12803c3fcd9SGreentime Hu "vle8.v v16, (%1)\n\t"
12903c3fcd9SGreentime Hu "add %1, %1, %0\n\t"
13003c3fcd9SGreentime Hu "vle8.v v24, (%1)\n\t"
13103c3fcd9SGreentime Hu ".option pop\n\t"
13203c3fcd9SGreentime Hu : "=&r" (vl) : "r" (datap) : "memory");
13303c3fcd9SGreentime Hu __vstate_csr_restore(restore_from);
13403c3fcd9SGreentime Hu riscv_v_disable();
13503c3fcd9SGreentime Hu }
13603c3fcd9SGreentime Hu
__riscv_v_vstate_discard(void)1379657e9b7SBjörn Töpel static inline void __riscv_v_vstate_discard(void)
1389657e9b7SBjörn Töpel {
1399657e9b7SBjörn Töpel unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
1409657e9b7SBjörn Töpel
1419657e9b7SBjörn Töpel riscv_v_enable();
1429657e9b7SBjörn Töpel asm volatile (
1439657e9b7SBjörn Töpel ".option push\n\t"
1449657e9b7SBjörn Töpel ".option arch, +v\n\t"
1459657e9b7SBjörn Töpel "vsetvli %0, x0, e8, m8, ta, ma\n\t"
1469657e9b7SBjörn Töpel "vmv.v.i v0, -1\n\t"
1479657e9b7SBjörn Töpel "vmv.v.i v8, -1\n\t"
1489657e9b7SBjörn Töpel "vmv.v.i v16, -1\n\t"
1499657e9b7SBjörn Töpel "vmv.v.i v24, -1\n\t"
1509657e9b7SBjörn Töpel "vsetvl %0, x0, %1\n\t"
1519657e9b7SBjörn Töpel ".option pop\n\t"
1529657e9b7SBjörn Töpel : "=&r" (vl) : "r" (vtype_inval) : "memory");
1539657e9b7SBjörn Töpel riscv_v_disable();
1549657e9b7SBjörn Töpel }
1559657e9b7SBjörn Töpel
riscv_v_vstate_discard(struct pt_regs * regs)1569657e9b7SBjörn Töpel static inline void riscv_v_vstate_discard(struct pt_regs *regs)
1579657e9b7SBjörn Töpel {
1589657e9b7SBjörn Töpel if ((regs->status & SR_VS) == SR_VS_OFF)
1599657e9b7SBjörn Töpel return;
1609657e9b7SBjörn Töpel
1619657e9b7SBjörn Töpel __riscv_v_vstate_discard();
1629657e9b7SBjörn Töpel __riscv_v_vstate_dirty(regs);
1639657e9b7SBjörn Töpel }
1649657e9b7SBjörn Töpel
riscv_v_vstate_save(struct task_struct * task,struct pt_regs * regs)1653a2df632SGreentime Hu static inline void riscv_v_vstate_save(struct task_struct *task,
1663a2df632SGreentime Hu struct pt_regs *regs)
1673a2df632SGreentime Hu {
1683a2df632SGreentime Hu if ((regs->status & SR_VS) == SR_VS_DIRTY) {
1693a2df632SGreentime Hu struct __riscv_v_ext_state *vstate = &task->thread.vstate;
1703a2df632SGreentime Hu
1713a2df632SGreentime Hu __riscv_v_vstate_save(vstate, vstate->datap);
1723a2df632SGreentime Hu __riscv_v_vstate_clean(regs);
1733a2df632SGreentime Hu }
1743a2df632SGreentime Hu }
1753a2df632SGreentime Hu
riscv_v_vstate_restore(struct task_struct * task,struct pt_regs * regs)1763a2df632SGreentime Hu static inline void riscv_v_vstate_restore(struct task_struct *task,
1773a2df632SGreentime Hu struct pt_regs *regs)
1783a2df632SGreentime Hu {
1793a2df632SGreentime Hu if ((regs->status & SR_VS) != SR_VS_OFF) {
1803a2df632SGreentime Hu struct __riscv_v_ext_state *vstate = &task->thread.vstate;
1813a2df632SGreentime Hu
1823a2df632SGreentime Hu __riscv_v_vstate_restore(vstate, vstate->datap);
1833a2df632SGreentime Hu __riscv_v_vstate_clean(regs);
1843a2df632SGreentime Hu }
1853a2df632SGreentime Hu }
1863a2df632SGreentime Hu
__switch_to_vector(struct task_struct * prev,struct task_struct * next)1873a2df632SGreentime Hu static inline void __switch_to_vector(struct task_struct *prev,
1883a2df632SGreentime Hu struct task_struct *next)
1893a2df632SGreentime Hu {
1903a2df632SGreentime Hu struct pt_regs *regs;
1913a2df632SGreentime Hu
1923a2df632SGreentime Hu regs = task_pt_regs(prev);
1933a2df632SGreentime Hu riscv_v_vstate_save(prev, regs);
1943a2df632SGreentime Hu riscv_v_vstate_restore(next, task_pt_regs(next));
1953a2df632SGreentime Hu }
1963a2df632SGreentime Hu
1971fd96a3eSAndy Chiu void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
1981fd96a3eSAndy Chiu bool riscv_v_vstate_ctrl_user_allowed(void);
1991fd96a3eSAndy Chiu
200dc6667a4SGuo Ren #else /* ! CONFIG_RISCV_ISA_V */
201dc6667a4SGuo Ren
2027017858eSGreentime Hu struct pt_regs;
2037017858eSGreentime Hu
riscv_v_setup_vsize(void)2047017858eSGreentime Hu static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
has_vector(void)205dc6667a4SGuo Ren static __always_inline bool has_vector(void) { return false; }
riscv_v_first_use_handler(struct pt_regs * regs)206cd054837SAndy Chiu static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
riscv_v_vstate_query(struct pt_regs * regs)20703c3fcd9SGreentime Hu static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
riscv_v_vstate_ctrl_user_allowed(void)2081fd96a3eSAndy Chiu static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
2097017858eSGreentime Hu #define riscv_v_vsize (0)
2109657e9b7SBjörn Töpel #define riscv_v_vstate_discard(regs) do {} while (0)
2113a2df632SGreentime Hu #define riscv_v_vstate_save(task, regs) do {} while (0)
2123a2df632SGreentime Hu #define riscv_v_vstate_restore(task, regs) do {} while (0)
2133a2df632SGreentime Hu #define __switch_to_vector(__prev, __next) do {} while (0)
21403c3fcd9SGreentime Hu #define riscv_v_vstate_off(regs) do {} while (0)
21503c3fcd9SGreentime Hu #define riscv_v_vstate_on(regs) do {} while (0)
216dc6667a4SGuo Ren
217dc6667a4SGuo Ren #endif /* CONFIG_RISCV_ISA_V */
218dc6667a4SGuo Ren
219dc6667a4SGuo Ren #endif /* ! __ASM_RISCV_VECTOR_H */
220