xref: /openbmc/linux/arch/csky/abiv2/fpu.c (revision 7effbd18)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3 
4 #include <linux/ptrace.h>
5 #include <linux/uaccess.h>
6 #include <abi/reg_ops.h>
7 
8 #define MTCR_MASK	0xFC00FFE0
9 #define MFCR_MASK	0xFC00FFE0
10 #define MTCR_DIST	0xC0006420
11 #define MFCR_DIST	0xC0006020
12 
13 /*
14  * fpu_libc_helper() is to help libc to excute:
15  *  - mfcr %a, cr<1, 2>
16  *  - mfcr %a, cr<2, 2>
17  *  - mtcr %a, cr<1, 2>
18  *  - mtcr %a, cr<2, 2>
19  */
20 int fpu_libc_helper(struct pt_regs *regs)
21 {
22 	int fault;
23 	unsigned long instrptr, regx = 0;
24 	unsigned long index = 0, tmp = 0;
25 	unsigned long tinstr = 0;
26 	u16 instr_hi, instr_low;
27 
28 	instrptr = instruction_pointer(regs);
29 	if (instrptr & 1)
30 		return 0;
31 
32 	fault = __get_user(instr_low, (u16 *)instrptr);
33 	if (fault)
34 		return 0;
35 
36 	fault = __get_user(instr_hi, (u16 *)(instrptr + 2));
37 	if (fault)
38 		return 0;
39 
40 	tinstr = instr_hi | ((unsigned long)instr_low << 16);
41 
42 	if (((tinstr >> 21) & 0x1F) != 2)
43 		return 0;
44 
45 	if ((tinstr & MTCR_MASK) == MTCR_DIST) {
46 		index = (tinstr >> 16) & 0x1F;
47 		if (index > 13)
48 			return 0;
49 
50 		tmp = tinstr & 0x1F;
51 		if (tmp > 2)
52 			return 0;
53 
54 		regx =  *(&regs->a0 + index);
55 
56 		if (tmp == 1)
57 			mtcr("cr<1, 2>", regx);
58 		else if (tmp == 2)
59 			mtcr("cr<2, 2>", regx);
60 		else
61 			return 0;
62 
63 		regs->pc += 4;
64 		return 1;
65 	}
66 
67 	if ((tinstr & MFCR_MASK) == MFCR_DIST) {
68 		index = tinstr & 0x1F;
69 		if (index > 13)
70 			return 0;
71 
72 		tmp = ((tinstr >> 16) & 0x1F);
73 		if (tmp > 2)
74 			return 0;
75 
76 		if (tmp == 1)
77 			regx = mfcr("cr<1, 2>");
78 		else if (tmp == 2)
79 			regx = mfcr("cr<2, 2>");
80 		else
81 			return 0;
82 
83 		*(&regs->a0 + index) = regx;
84 
85 		regs->pc += 4;
86 		return 1;
87 	}
88 
89 	return 0;
90 }
91 
92 void fpu_fpe(struct pt_regs *regs)
93 {
94 	int sig, code;
95 	unsigned int fesr;
96 
97 	fesr = mfcr("cr<2, 2>");
98 
99 	sig = SIGFPE;
100 	code = FPE_FLTUNK;
101 
102 	if (fesr & FPE_ILLE) {
103 		sig = SIGILL;
104 		code = ILL_ILLOPC;
105 	} else if (fesr & FPE_IDC) {
106 		sig = SIGILL;
107 		code = ILL_ILLOPN;
108 	} else if (fesr & FPE_FEC) {
109 		sig = SIGFPE;
110 		if (fesr & FPE_IOC)
111 			code = FPE_FLTINV;
112 		else if (fesr & FPE_DZC)
113 			code = FPE_FLTDIV;
114 		else if (fesr & FPE_UFC)
115 			code = FPE_FLTUND;
116 		else if (fesr & FPE_OFC)
117 			code = FPE_FLTOVF;
118 		else if (fesr & FPE_IXC)
119 			code = FPE_FLTRES;
120 	}
121 
122 	force_sig_fault(sig, code, (void __user *)regs->pc);
123 }
124 
125 #define FMFVR_FPU_REGS(vrx, vry)	\
126 	"fmfvrl %0, "#vrx"\n"		\
127 	"fmfvrh %1, "#vrx"\n"		\
128 	"fmfvrl %2, "#vry"\n"		\
129 	"fmfvrh %3, "#vry"\n"
130 
131 #define FMTVR_FPU_REGS(vrx, vry)	\
132 	"fmtvrl "#vrx", %0\n"		\
133 	"fmtvrh "#vrx", %1\n"		\
134 	"fmtvrl "#vry", %2\n"		\
135 	"fmtvrh "#vry", %3\n"
136 
137 #define STW_FPU_REGS(a, b, c, d)	\
138 	"stw    %0, (%4, "#a")\n"	\
139 	"stw    %1, (%4, "#b")\n"	\
140 	"stw    %2, (%4, "#c")\n"	\
141 	"stw    %3, (%4, "#d")\n"
142 
143 #define LDW_FPU_REGS(a, b, c, d)	\
144 	"ldw    %0, (%4, "#a")\n"	\
145 	"ldw    %1, (%4, "#b")\n"	\
146 	"ldw    %2, (%4, "#c")\n"	\
147 	"ldw    %3, (%4, "#d")\n"
148 
149 void save_to_user_fp(struct user_fp *user_fp)
150 {
151 	unsigned long flg;
152 	unsigned long tmp1, tmp2;
153 	unsigned long *fpregs;
154 
155 	local_irq_save(flg);
156 
157 	tmp1 = mfcr("cr<1, 2>");
158 	tmp2 = mfcr("cr<2, 2>");
159 
160 	user_fp->fcr = tmp1;
161 	user_fp->fesr = tmp2;
162 
163 	fpregs = &user_fp->vr[0];
164 #ifdef CONFIG_CPU_HAS_FPUV2
165 #ifdef CONFIG_CPU_HAS_VDSP
166 	asm volatile(
167 		"vstmu.32    vr0-vr3,   (%0)\n"
168 		"vstmu.32    vr4-vr7,   (%0)\n"
169 		"vstmu.32    vr8-vr11,  (%0)\n"
170 		"vstmu.32    vr12-vr15, (%0)\n"
171 		"fstmu.64    vr16-vr31, (%0)\n"
172 		: "+a"(fpregs)
173 		::"memory");
174 #else
175 	asm volatile(
176 		"fstmu.64    vr0-vr31,  (%0)\n"
177 		: "+a"(fpregs)
178 		::"memory");
179 #endif
180 #else
181 	{
182 	unsigned long tmp3, tmp4;
183 
184 	asm volatile(
185 		FMFVR_FPU_REGS(vr0, vr1)
186 		STW_FPU_REGS(0, 4, 16, 20)
187 		FMFVR_FPU_REGS(vr2, vr3)
188 		STW_FPU_REGS(32, 36, 48, 52)
189 		FMFVR_FPU_REGS(vr4, vr5)
190 		STW_FPU_REGS(64, 68, 80, 84)
191 		FMFVR_FPU_REGS(vr6, vr7)
192 		STW_FPU_REGS(96, 100, 112, 116)
193 		"addi	%4, 128\n"
194 		FMFVR_FPU_REGS(vr8, vr9)
195 		STW_FPU_REGS(0, 4, 16, 20)
196 		FMFVR_FPU_REGS(vr10, vr11)
197 		STW_FPU_REGS(32, 36, 48, 52)
198 		FMFVR_FPU_REGS(vr12, vr13)
199 		STW_FPU_REGS(64, 68, 80, 84)
200 		FMFVR_FPU_REGS(vr14, vr15)
201 		STW_FPU_REGS(96, 100, 112, 116)
202 		: "=a"(tmp1), "=a"(tmp2), "=a"(tmp3),
203 		  "=a"(tmp4), "+a"(fpregs)
204 		::"memory");
205 	}
206 #endif
207 
208 	local_irq_restore(flg);
209 }
210 
211 void restore_from_user_fp(struct user_fp *user_fp)
212 {
213 	unsigned long flg;
214 	unsigned long tmp1, tmp2;
215 	unsigned long *fpregs;
216 
217 	local_irq_save(flg);
218 
219 	tmp1 = user_fp->fcr;
220 	tmp2 = user_fp->fesr;
221 
222 	mtcr("cr<1, 2>", tmp1);
223 	mtcr("cr<2, 2>", tmp2);
224 
225 	fpregs = &user_fp->vr[0];
226 #ifdef CONFIG_CPU_HAS_FPUV2
227 #ifdef CONFIG_CPU_HAS_VDSP
228 	asm volatile(
229 		"vldmu.32    vr0-vr3,   (%0)\n"
230 		"vldmu.32    vr4-vr7,   (%0)\n"
231 		"vldmu.32    vr8-vr11,  (%0)\n"
232 		"vldmu.32    vr12-vr15, (%0)\n"
233 		"fldmu.64    vr16-vr31, (%0)\n"
234 		: "+a"(fpregs)
235 		::"memory");
236 #else
237 	asm volatile(
238 		"fldmu.64    vr0-vr31,  (%0)\n"
239 		: "+a"(fpregs)
240 		::"memory");
241 #endif
242 #else
243 	{
244 	unsigned long tmp3, tmp4;
245 
246 	asm volatile(
247 		LDW_FPU_REGS(0, 4, 16, 20)
248 		FMTVR_FPU_REGS(vr0, vr1)
249 		LDW_FPU_REGS(32, 36, 48, 52)
250 		FMTVR_FPU_REGS(vr2, vr3)
251 		LDW_FPU_REGS(64, 68, 80, 84)
252 		FMTVR_FPU_REGS(vr4, vr5)
253 		LDW_FPU_REGS(96, 100, 112, 116)
254 		FMTVR_FPU_REGS(vr6, vr7)
255 		"addi	%4, 128\n"
256 		LDW_FPU_REGS(0, 4, 16, 20)
257 		FMTVR_FPU_REGS(vr8, vr9)
258 		LDW_FPU_REGS(32, 36, 48, 52)
259 		FMTVR_FPU_REGS(vr10, vr11)
260 		LDW_FPU_REGS(64, 68, 80, 84)
261 		FMTVR_FPU_REGS(vr12, vr13)
262 		LDW_FPU_REGS(96, 100, 112, 116)
263 		FMTVR_FPU_REGS(vr14, vr15)
264 		: "=a"(tmp1), "=a"(tmp2), "=a"(tmp3),
265 		  "=a"(tmp4), "+a"(fpregs)
266 		::"memory");
267 	}
268 #endif
269 	local_irq_restore(flg);
270 }
271