xref: /netbsd-src/sys/arch/riscv/riscv/fpu.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: fpu.c,v 1.1 2015/03/28 16:13:56 matt Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 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: fpu.c,v 1.1 2015/03/28 16:13:56 matt Exp $");
34 
35 #include "opt_multiprocessor.h"
36 
37 #include <sys/param.h>
38 #include <sys/mutex.h>
39 #include <sys/condvar.h>
40 #include <sys/cpu.h>
41 #include <sys/proc.h>
42 #include <sys/lwp.h>
43 #include <sys/pcu.h>
44 
45 #include <riscv/locore.h>
46 
47 static void fpu_state_save(lwp_t *);
48 static void fpu_state_load(lwp_t *, u_int);
49 static void fpu_state_release(lwp_t *);
50 
51 const pcu_ops_t pcu_fpu_ops = {
52 	.pcu_id = PCU_FPU,
53 	.pcu_state_save = fpu_state_save,
54 	.pcu_state_load = fpu_state_load,
55 	.pcu_state_release = fpu_state_release
56 };
57 
58 void
59 fpu_state_save(lwp_t *l)
60 {
61 	struct trapframe * const tf = l->l_md.md_utf;
62 	struct pcb * const pcb = lwp_getpcb(l);
63 	struct fpreg * const fp = &pcb->pcb_fpregs;
64 
65 	KASSERT(l->l_pcu_cpu[PCU_FPU] == curcpu());
66 
67 	// Don't do anything if the FPU is already off.
68 	if ((tf->tf_sr & SR_EF) == 0)
69 		return;
70 
71 	curcpu()->ci_ev_fpu_saves.ev_count++;
72 
73 	// Enable FPU to save FP state
74 	(void) riscvreg_status_set(SR_EF);
75 
76 	// Save FCSR
77 	fp->r_fcsr = riscvreg_fcsr_read();
78 
79 	// Save FP register values.
80 	__asm(	"fsd	f0, (0*%1)(%0)"
81 	"\n\t"	"fsd	f1, (1*%1)(%0)"
82 	"\n\t"	"fsd	f2, (2*%1)(%0)"
83 	"\n\t"	"fsd	f3, (3*%1)(%0)"
84 	"\n\t"	"fsd	f4, (4*%1)(%0)"
85 	"\n\t"	"fsd	f5, (5*%1)(%0)"
86 	"\n\t"	"fsd	f6, (6*%1)(%0)"
87 	"\n\t"	"fsd	f7, (7*%1)(%0)"
88 	"\n\t"	"fsd	f8, (8*%1)(%0)"
89 	"\n\t"	"fsd	f9, (9*%1)(%0)"
90 	"\n\t"	"fsd	f10, (10*%1)(%0)"
91 	"\n\t"	"fsd	f11, (11*%1)(%0)"
92 	"\n\t"	"fsd	f12, (12*%1)(%0)"
93 	"\n\t"	"fsd	f13, (13*%1)(%0)"
94 	"\n\t"	"fsd	f14, (14*%1)(%0)"
95 	"\n\t"	"fsd	f15, (15*%1)(%0)"
96 	"\n\t"	"fsd	f16, (16*%1)(%0)"
97 	"\n\t"	"fsd	f17, (17*%1)(%0)"
98 	"\n\t"	"fsd	f18, (18*%1)(%0)"
99 	"\n\t"	"fsd	f19, (19*%1)(%0)"
100 	"\n\t"	"fsd	f20, (20*%1)(%0)"
101 	"\n\t"	"fsd	f21, (21*%1)(%0)"
102 	"\n\t"	"fsd	f22, (22*%1)(%0)"
103 	"\n\t"	"fsd	f23, (23*%1)(%0)"
104 	"\n\t"	"fsd	f24, (24*%1)(%0)"
105 	"\n\t"	"fsd	f25, (25*%1)(%0)"
106 	"\n\t"	"fsd	f26, (26*%1)(%0)"
107 	"\n\t"	"fsd	f27, (27*%1)(%0)"
108 	"\n\t"	"fsd	f28, (28*%1)(%0)"
109 	"\n\t"	"fsd	f29, (29*%1)(%0)"
110 	"\n\t"	"fsd	f30, (30*%1)(%0)"
111 	"\n\t"	"fsd	f31, (31*%1)(%0)"
112 	   ::	"r"(fp->r_fpreg),
113 		"i"(sizeof(fp->r_fpreg[0])));
114 
115 	// Disable the FPU
116 	riscvreg_status_clear(SR_EF);
117 }
118 
119 void
120 fpu_state_load(lwp_t *l, u_int flags)
121 {
122 	struct trapframe * const tf = l->l_md.md_utf;
123 	struct pcb * const pcb = lwp_getpcb(l);
124 	struct fpreg * const fp = &pcb->pcb_fpregs;
125 
126 	KASSERT(l->l_pcu_cpu[PCU_FPU] == curcpu());
127 
128 	// If this is the first time the state is being loaded, zero it first.
129 	if (__predict_false((flags & PCU_VALID) == 0)) {
130 		memset(fp, 0, sizeof(*fp));
131 	}
132 
133 	// Enable the FP when this lwp return to userspace.
134 	tf->tf_sr |= SR_EF;
135 
136 	// If this is a simple reeanble, set the FPU enable flag and return
137 	if (flags & PCU_REENABLE) {
138 		curcpu()->ci_ev_fpu_reenables.ev_count++;
139 		return;
140 	}
141 
142 	curcpu()->ci_ev_fpu_loads.ev_count++;
143 
144 
145 	// Enabling to load FP state.  Interrupts will remain on.
146 	(void) riscvreg_status_set(SR_EF);
147 
148 	// load FP registers and establish processes' FP context.
149 	__asm(	"fld	f0, (0*%1)(%0)"
150 	"\n\t"	"fld	f1, (1*%1)(%0)"
151 	"\n\t"	"fld	f2, (2*%1)(%0)"
152 	"\n\t"	"fld	f3, (3*%1)(%0)"
153 	"\n\t"	"fld	f4, (4*%1)(%0)"
154 	"\n\t"	"fld	f5, (5*%1)(%0)"
155 	"\n\t"	"fld	f6, (6*%1)(%0)"
156 	"\n\t"	"fld	f7, (7*%1)(%0)"
157 	"\n\t"	"fld	f8, (8*%1)(%0)"
158 	"\n\t"	"fld	f9, (9*%1)(%0)"
159 	"\n\t"	"fld	f10, (10*%1)(%0)"
160 	"\n\t"	"fld	f11, (11*%1)(%0)"
161 	"\n\t"	"fld	f12, (12*%1)(%0)"
162 	"\n\t"	"fld	f13, (13*%1)(%0)"
163 	"\n\t"	"fld	f14, (14*%1)(%0)"
164 	"\n\t"	"fld	f15, (15*%1)(%0)"
165 	"\n\t"	"fld	f16, (16*%1)(%0)"
166 	"\n\t"	"fld	f17, (17*%1)(%0)"
167 	"\n\t"	"fld	f18, (18*%1)(%0)"
168 	"\n\t"	"fld	f19, (19*%1)(%0)"
169 	"\n\t"	"fld	f20, (20*%1)(%0)"
170 	"\n\t"	"fld	f21, (21*%1)(%0)"
171 	"\n\t"	"fld	f22, (22*%1)(%0)"
172 	"\n\t"	"fld	f23, (23*%1)(%0)"
173 	"\n\t"	"fld	f24, (24*%1)(%0)"
174 	"\n\t"	"fld	f25, (25*%1)(%0)"
175 	"\n\t"	"fld	f26, (26*%1)(%0)"
176 	"\n\t"	"fld	f27, (27*%1)(%0)"
177 	"\n\t"	"fld	f28, (28*%1)(%0)"
178 	"\n\t"	"fld	f29, (29*%1)(%0)"
179 	"\n\t"	"fld	f30, (30*%1)(%0)"
180 	"\n\t"	"fld	f31, (31*%1)(%0)"
181 	   ::	"r"(fp->r_fpreg),
182 		"i"(sizeof(fp->r_fpreg[0])));
183 
184 	// load FPCSR and disable FPU again
185 	riscvreg_fcsr_write(fp->r_fcsr);
186 	riscvreg_status_clear(SR_EF);
187 }
188 
189 void
190 fpu_state_release(lwp_t *l)
191 {
192 	l->l_md.md_utf->tf_sr &= ~SR_EF;
193 }
194