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
54static void vec_state_load(lwp_t *, u_int);
55static void vec_state_save(lwp_t *);
56static void vec_state_release(lwp_t *);
57
58const 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
65bool
66vec_used_p(lwp_t *l)
67{
68	return pcu_valid_p(&vec_ops, l);
69}
70
71void
72vec_mark_used(lwp_t *l)
73{
74	pcu_discard(&vec_ops, l, true);
75}
76
77void
78vec_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
113void
114vec_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
138void
139vec_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
148void
149vec_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
173bool
174vec_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
217static 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
229int
230vec_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