riscv: Allocate user's vector context in the first-use trap
[platform/kernel/linux-starfive.git] / arch / riscv / kernel / vector.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2023 SiFive
4  * Author: Andy Chiu <andy.chiu@sifive.com>
5  */
6 #include <linux/export.h>
7 #include <linux/sched/signal.h>
8 #include <linux/types.h>
9 #include <linux/slab.h>
10 #include <linux/sched.h>
11 #include <linux/uaccess.h>
12
13 #include <asm/thread_info.h>
14 #include <asm/processor.h>
15 #include <asm/insn.h>
16 #include <asm/vector.h>
17 #include <asm/csr.h>
18 #include <asm/elf.h>
19 #include <asm/ptrace.h>
20 #include <asm/bug.h>
21
22 unsigned long riscv_v_vsize __read_mostly;
23 EXPORT_SYMBOL_GPL(riscv_v_vsize);
24
25 int riscv_v_setup_vsize(void)
26 {
27         unsigned long this_vsize;
28
29         /* There are 32 vector registers with vlenb length. */
30         riscv_v_enable();
31         this_vsize = csr_read(CSR_VLENB) * 32;
32         riscv_v_disable();
33
34         if (!riscv_v_vsize) {
35                 riscv_v_vsize = this_vsize;
36                 return 0;
37         }
38
39         if (riscv_v_vsize != this_vsize) {
40                 WARN(1, "RISCV_ISA_V only supports one vlenb on SMP systems");
41                 return -EOPNOTSUPP;
42         }
43
44         return 0;
45 }
46
47 static bool insn_is_vector(u32 insn_buf)
48 {
49         u32 opcode = insn_buf & __INSN_OPCODE_MASK;
50         u32 width, csr;
51
52         /*
53          * All V-related instructions, including CSR operations are 4-Byte. So,
54          * do not handle if the instruction length is not 4-Byte.
55          */
56         if (unlikely(GET_INSN_LENGTH(insn_buf) != 4))
57                 return false;
58
59         switch (opcode) {
60         case RVV_OPCODE_VECTOR:
61                 return true;
62         case RVV_OPCODE_VL:
63         case RVV_OPCODE_VS:
64                 width = RVV_EXRACT_VL_VS_WIDTH(insn_buf);
65                 if (width == RVV_VL_VS_WIDTH_8 || width == RVV_VL_VS_WIDTH_16 ||
66                     width == RVV_VL_VS_WIDTH_32 || width == RVV_VL_VS_WIDTH_64)
67                         return true;
68
69                 break;
70         case RVG_OPCODE_SYSTEM:
71                 csr = RVG_EXTRACT_SYSTEM_CSR(insn_buf);
72                 if ((csr >= CSR_VSTART && csr <= CSR_VCSR) ||
73                     (csr >= CSR_VL && csr <= CSR_VLENB))
74                         return true;
75         }
76
77         return false;
78 }
79
80 static int riscv_v_thread_zalloc(void)
81 {
82         void *datap;
83
84         datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
85         if (!datap)
86                 return -ENOMEM;
87
88         current->thread.vstate.datap = datap;
89         memset(&current->thread.vstate, 0, offsetof(struct __riscv_v_ext_state,
90                                                     datap));
91         return 0;
92 }
93
94 bool riscv_v_first_use_handler(struct pt_regs *regs)
95 {
96         u32 __user *epc = (u32 __user *)regs->epc;
97         u32 insn = (u32)regs->badaddr;
98
99         /* Do not handle if V is not supported, or disabled */
100         if (!(ELF_HWCAP & COMPAT_HWCAP_ISA_V))
101                 return false;
102
103         /* If V has been enabled then it is not the first-use trap */
104         if (riscv_v_vstate_query(regs))
105                 return false;
106
107         /* Get the instruction */
108         if (!insn) {
109                 if (__get_user(insn, epc))
110                         return false;
111         }
112
113         /* Filter out non-V instructions */
114         if (!insn_is_vector(insn))
115                 return false;
116
117         /* Sanity check. datap should be null by the time of the first-use trap */
118         WARN_ON(current->thread.vstate.datap);
119
120         /*
121          * Now we sure that this is a V instruction. And it executes in the
122          * context where VS has been off. So, try to allocate the user's V
123          * context and resume execution.
124          */
125         if (riscv_v_thread_zalloc()) {
126                 force_sig(SIGBUS);
127                 return true;
128         }
129         riscv_v_vstate_on(regs);
130         return true;
131 }