1 /* $NetBSD: spe.c,v 1.11 2020/07/06 09:34:16 rin Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: spe.c,v 1.11 2020/07/06 09:34:16 rin Exp $");
34
35 #ifdef _KERNEL_OPT
36 #include "opt_altivec.h"
37 #endif
38
39 #ifdef PPC_HAVE_SPE
40
41 #include <sys/param.h>
42 #include <sys/proc.h>
43 #include <sys/systm.h>
44 #include <sys/atomic.h>
45 #include <sys/siginfo.h>
46 #include <sys/pcu.h>
47
48 #include <powerpc/altivec.h>
49 #include <powerpc/spr.h>
50 #include <powerpc/booke/spr.h>
51 #include <powerpc/psl.h>
52 #include <powerpc/pcb.h>
53
54 static void vec_state_load(lwp_t *, u_int);
55 static void vec_state_save(lwp_t *);
56 static void vec_state_release(lwp_t *);
57
58 const pcu_ops_t vec_ops = {
59 .pcu_id = PCU_VEC,
60 .pcu_state_load = vec_state_load,
61 .pcu_state_save = vec_state_save,
62 .pcu_state_release = vec_state_release,
63 };
64
65 bool
vec_used_p(lwp_t * l)66 vec_used_p(lwp_t *l)
67 {
68 return pcu_valid_p(&vec_ops, l);
69 }
70
71 void
vec_mark_used(lwp_t * l)72 vec_mark_used(lwp_t *l)
73 {
74 pcu_discard(&vec_ops, l, true);
75 }
76
77 void
vec_state_load(lwp_t * l,u_int flags)78 vec_state_load(lwp_t *l, u_int flags)
79 {
80 struct pcb * const pcb = lwp_getpcb(l);
81
82 if ((flags & PCU_VALID) == 0) {
83 memset(&pcb->pcb_vr, 0, sizeof(pcb->pcb_vr));
84 vec_mark_used(l);
85 }
86
87 /*
88 * Enable SPE temporarily (and disable interrupts).
89 */
90 const register_t msr = mfmsr();
91 mtmsr((msr & ~PSL_EE) | PSL_SPV);
92 __asm volatile ("isync");
93
94 /*
95 * Call an assembly routine to do load everything.
96 */
97 vec_load_from_vreg(&pcb->pcb_vr);
98 __asm volatile ("sync");
99
100
101 /*
102 * Restore MSR (turn off SPE)
103 */
104 mtmsr(msr);
105 __asm volatile ("isync");
106
107 /*
108 * Set PSL_SPV so vectors will be enabled on return to user.
109 */
110 l->l_md.md_utf->tf_srr1 |= PSL_SPV;
111 }
112
113 void
vec_state_save(lwp_t * l)114 vec_state_save(lwp_t *l)
115 {
116 struct pcb * const pcb = lwp_getpcb(l);
117
118 /*
119 * Turn on SPE, turn off interrupts.
120 */
121 const register_t msr = mfmsr();
122 mtmsr((msr & ~PSL_EE) | PSL_SPV);
123 __asm volatile ("isync");
124
125 /*
126 * Save the vector state which is best done in assembly.
127 */
128 vec_unload_to_vreg(&pcb->pcb_vr);
129 __asm volatile ("sync");
130
131 /*
132 * Restore MSR (turn off SPE)
133 */
134 mtmsr(msr);
135 __asm volatile ("isync");
136 }
137
138 void
vec_state_release(lwp_t * l)139 vec_state_release(lwp_t *l)
140 {
141 /*
142 * Turn off SPV so the next SPE instruction will cause a
143 * SPE unavailable exception
144 */
145 l->l_md.md_utf->tf_srr1 &= ~PSL_SPV;
146 }
147
148 void
vec_restore_from_mcontext(lwp_t * l,const mcontext_t * mcp)149 vec_restore_from_mcontext(lwp_t *l, const mcontext_t *mcp)
150 {
151 struct pcb * const pcb = lwp_getpcb(l);
152 const union __vr *vr = mcp->__vrf.__vrs;
153
154 vec_save(l);
155
156 /* grab the accumulator */
157 pcb->pcb_vr.vreg[8][0] = vr->__vr32[2];
158 pcb->pcb_vr.vreg[8][1] = vr->__vr32[3];
159
160 /*
161 * We store the high parts of each register in the first 8 vectors.
162 */
163 for (u_int i = 0; i < 8; i++, vr += 4) {
164 pcb->pcb_vr.vreg[i][0] = vr[0].__vr32[0];
165 pcb->pcb_vr.vreg[i][1] = vr[1].__vr32[0];
166 pcb->pcb_vr.vreg[i][2] = vr[2].__vr32[0];
167 pcb->pcb_vr.vreg[i][3] = vr[3].__vr32[0];
168 }
169 l->l_md.md_utf->tf_spefscr = pcb->pcb_vr.vscr = mcp->__vrf.__vscr;
170 pcb->pcb_vr.vrsave = mcp->__vrf.__vrsave;
171 }
172
173 bool
vec_save_to_mcontext(lwp_t * l,mcontext_t * mcp,unsigned int * flagp)174 vec_save_to_mcontext(lwp_t *l, mcontext_t *mcp, unsigned int *flagp)
175 {
176 struct pcb * const pcb = lwp_getpcb(l);
177
178 if (!vec_used_p(l))
179 return false;
180
181 vec_save(l);
182
183 mcp->__gregs[_REG_MSR] |= PSL_SPV;
184
185 union __vr *vr = mcp->__vrf.__vrs;
186 const register_t *fixreg = l->l_md.md_utf->tf_fixreg;
187 for (u_int i = 0; i < 32; i++, vr += 4, fixreg += 4) {
188 vr[0].__vr32[0] = pcb->pcb_vr.vreg[i][0];
189 vr[0].__vr32[1] = fixreg[0];
190 vr[0].__vr32[2] = 0;
191 vr[0].__vr32[3] = 0;
192 vr[1].__vr32[0] = pcb->pcb_vr.vreg[i][1];
193 vr[1].__vr32[1] = fixreg[1];
194 vr[1].__vr32[2] = 0;
195 vr[1].__vr32[3] = 0;
196 vr[2].__vr32[0] = pcb->pcb_vr.vreg[i][2];
197 vr[2].__vr32[1] = fixreg[2];
198 vr[2].__vr32[2] = 0;
199 vr[2].__vr32[3] = 0;
200 vr[3].__vr32[0] = pcb->pcb_vr.vreg[i][3];
201 vr[3].__vr32[1] = fixreg[3];
202 vr[3].__vr32[2] = 0;
203 vr[3].__vr32[3] = 0;
204 }
205
206 mcp->__vrf.__vrs[0].__vr32[2] = pcb->pcb_vr.vreg[8][0];
207 mcp->__vrf.__vrs[0].__vr32[3] = pcb->pcb_vr.vreg[8][1];
208
209 mcp->__vrf.__vrsave = pcb->pcb_vr.vrsave;
210 mcp->__vrf.__vscr = l->l_md.md_utf->tf_spefscr;
211
212 *flagp |= _UC_POWERPC_SPE;
213
214 return true;
215 }
216
217 static const struct {
218 uint32_t mask;
219 int code;
220 } spefscr_siginfo_map[] = {
221 { SPEFSCR_FINV|SPEFSCR_FINVH, FPE_FLTINV },
222 { SPEFSCR_FOVF|SPEFSCR_FOVFH, FPE_FLTOVF },
223 { SPEFSCR_FUNF|SPEFSCR_FUNFH, FPE_FLTUND },
224 { SPEFSCR_FX |SPEFSCR_FXH, FPE_FLTRES },
225 { SPEFSCR_FDBZ|SPEFSCR_FDBZH, FPE_FLTDIV },
226 { SPEFSCR_OV |SPEFSCR_OVH, FPE_INTOVF },
227 };
228
229 int
vec_siginfo_code(const struct trapframe * tf)230 vec_siginfo_code(const struct trapframe *tf)
231 {
232 for (u_int i = 0; i < __arraycount(spefscr_siginfo_map); i++) {
233 if (tf->tf_spefscr & spefscr_siginfo_map[i].mask)
234 return spefscr_siginfo_map[i].code;
235 }
236 return 0;
237 }
238
239 #endif /* PPC_HAVE_SPE */
240