1/*	$NetBSD: spe.c,v 1.4 2011/05/02 06:43:16 matt 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.4 2011/05/02 06:43:16 matt Exp $");
34
35#include "opt_altivec.h"
36
37#ifdef PPC_HAVE_SPE
38
39#include <sys/param.h>
40#include <sys/proc.h>
41#include <sys/systm.h>
42#include <sys/atomic.h>
43#include <sys/siginfo.h>
44#include <sys/pcu.h>
45
46#include <powerpc/altivec.h>
47#include <powerpc/spr.h>
48#include <powerpc/booke/spr.h>
49#include <powerpc/psl.h>
50#include <powerpc/pcb.h>
51
52static void vec_state_load(lwp_t *, bool);
53static void vec_state_save(lwp_t *);
54static void vec_state_release(lwp_t *);
55
56const pcu_ops_t vec_ops = {
57	.pcu_id = PCU_VEC,
58	.pcu_state_load = vec_state_load,
59	.pcu_state_save = vec_state_save,
60	.pcu_state_release = vec_state_release,
61};
62
63bool
64vec_used_p(lwp_t *l)
65{
66	return (l->l_md.md_flags & MDLWP_USEDVEC) != 0;
67}
68
69void
70vec_mark_used(lwp_t *l)
71{
72	l->l_md.md_flags |= MDLWP_USEDVEC;
73}
74
75void
76vec_state_load(lwp_t *l, bool used)
77{
78	struct pcb * const pcb = lwp_getpcb(l);
79
80	if (__predict_false(!vec_used_p(l))) {
81		memset(&pcb->pcb_vr, 0, sizeof(pcb->pcb_vr));
82		vec_mark_used(l);
83	}
84
85	/*
86	 * Enable SPE temporarily (and disable interrupts).
87	 */
88	const register_t msr = mfmsr();
89	mtmsr((msr & ~PSL_EE) | PSL_SPV);
90	__asm volatile ("isync");
91
92	/*
93	 * Call an assembly routine to do load everything.
94	 */
95	vec_load_from_vreg(&pcb->pcb_vr);
96	__asm volatile ("sync");
97
98
99	/*
100	 * Restore MSR (turn off SPE)
101	 */
102	mtmsr(msr);
103	__asm volatile ("isync");
104
105	/*
106	 * Note that vector has now been used.
107	 */
108	l->l_md.md_flags |= MDLWP_USEDVEC;
109	l->l_md.md_utf->tf_srr1 |= PSL_SPV;
110}
111
112void
113vec_state_save(lwp_t *l)
114{
115	struct pcb * const pcb = lwp_getpcb(l);
116
117	/*
118	 * Turn on SPE, turn off interrupts.
119	 */
120	const register_t msr = mfmsr();
121	mtmsr((msr & ~PSL_EE) | PSL_SPV);
122	__asm volatile ("isync");
123
124	/*
125	 * Save the vector state which is best done in assembly.
126	 */
127	vec_unload_to_vreg(&pcb->pcb_vr);
128	__asm volatile ("sync");
129
130	/*
131	 * Restore MSR (turn off SPE)
132	 */
133	mtmsr(msr);
134	__asm volatile ("isync");
135}
136
137void
138vec_state_release(lwp_t *l)
139{
140	/*
141	 * Turn off SPV so the next SPE instruction will cause a
142	 * SPE unavailable exception
143	 */
144	l->l_md.md_utf->tf_srr1 &= ~PSL_SPV;
145}
146
147void
148vec_restore_from_mcontext(lwp_t *l, const mcontext_t *mcp)
149{
150	struct pcb * const pcb = lwp_getpcb(l);
151	const union __vr *vr = mcp->__vrf.__vrs;
152
153	KASSERT(l == curlwp);
154
155	vec_save();
156
157	/* grab the accumulator */
158	pcb->pcb_vr.vreg[8][0] = vr->__vr32[2];
159	pcb->pcb_vr.vreg[8][1] = vr->__vr32[3];
160
161	/*
162	 * We store the high parts of each register in the first 8 vectors.
163	 */
164	for (u_int i = 0; i < 8; i++, vr += 4) {
165		pcb->pcb_vr.vreg[i][0] = vr[0].__vr32[0];
166		pcb->pcb_vr.vreg[i][1] = vr[1].__vr32[0];
167		pcb->pcb_vr.vreg[i][2] = vr[2].__vr32[0];
168		pcb->pcb_vr.vreg[i][3] = vr[3].__vr32[0];
169	}
170	l->l_md.md_utf->tf_spefscr = pcb->pcb_vr.vscr = mcp->__vrf.__vscr;
171	pcb->pcb_vr.vrsave = mcp->__vrf.__vrsave;
172}
173
174bool
175vec_save_to_mcontext(lwp_t *l, mcontext_t *mcp, unsigned int *flagp)
176{
177	struct pcb * const pcb = lwp_getpcb(l);
178
179	KASSERT(l == curlwp);
180
181	if (!vec_used_p(l))
182		return false;
183
184	vec_save();
185
186	mcp->__gregs[_REG_MSR] |= PSL_SPV;
187
188	union __vr *vr = mcp->__vrf.__vrs;
189	const register_t *fixreg = l->l_md.md_utf->tf_fixreg;
190	for (u_int i = 0; i < 32; i++, vr += 4, fixreg += 4) {
191		vr[0].__vr32[0] = pcb->pcb_vr.vreg[i][0];
192		vr[0].__vr32[1] = fixreg[0];
193		vr[0].__vr32[2] = 0;
194		vr[0].__vr32[3] = 0;
195		vr[1].__vr32[0] = pcb->pcb_vr.vreg[i][1];
196		vr[1].__vr32[1] = fixreg[1];
197		vr[1].__vr32[2] = 0;
198		vr[1].__vr32[3] = 0;
199		vr[2].__vr32[0] = pcb->pcb_vr.vreg[i][2];
200		vr[2].__vr32[1] = fixreg[2];
201		vr[2].__vr32[2] = 0;
202		vr[2].__vr32[3] = 0;
203		vr[3].__vr32[0] = pcb->pcb_vr.vreg[i][3];
204		vr[3].__vr32[1] = fixreg[3];
205		vr[3].__vr32[2] = 0;
206		vr[3].__vr32[3] = 0;
207	}
208
209	mcp->__vrf.__vrs[0].__vr32[2] = pcb->pcb_vr.vreg[8][0];
210	mcp->__vrf.__vrs[0].__vr32[3] = pcb->pcb_vr.vreg[8][1];
211
212	mcp->__vrf.__vrsave = pcb->pcb_vr.vrsave;
213	mcp->__vrf.__vscr = l->l_md.md_utf->tf_spefscr;
214
215	*flagp |= _UC_POWERPC_SPE;
216
217	return true;
218}
219
220static const struct {
221	uint32_t mask;
222	int code;
223} spefscr_siginfo_map[] = {
224	{ SPEFSCR_FINV|SPEFSCR_FINVH, FPE_FLTINV },
225	{ SPEFSCR_FOVF|SPEFSCR_FOVFH, FPE_FLTOVF },
226	{ SPEFSCR_FUNF|SPEFSCR_FUNFH, FPE_FLTUND },
227	{ SPEFSCR_FX  |SPEFSCR_FXH,   FPE_FLTRES },
228	{ SPEFSCR_FDBZ|SPEFSCR_FDBZH, FPE_FLTDIV },
229	{ SPEFSCR_OV  |SPEFSCR_OVH,   FPE_INTOVF },
230};
231
232int
233vec_siginfo_code(const struct trapframe *tf)
234{
235	for (u_int i = 0; i < __arraycount(spefscr_siginfo_map); i++) {
236		if (tf->tf_spefscr & spefscr_siginfo_map[i].mask)
237			return spefscr_siginfo_map[i].code;
238	}
239	return 0;
240}
241
242#endif /* PPC_HAVE_SPE */
243