1*75b842b8Sskrll /* $NetBSD: fpu.c,v 1.3 2023/05/07 12:41:49 skrll Exp $ */
29687a165Smatt
39687a165Smatt /*-
49687a165Smatt * Copyright (c) 2014 The NetBSD Foundation, Inc.
59687a165Smatt * All rights reserved.
69687a165Smatt *
79687a165Smatt * This code is derived from software contributed to The NetBSD Foundation
89687a165Smatt * by Matt Thomas of 3am Software Foundry.
99687a165Smatt *
109687a165Smatt * Redistribution and use in source and binary forms, with or without
119687a165Smatt * modification, are permitted provided that the following conditions
129687a165Smatt * are met:
139687a165Smatt * 1. Redistributions of source code must retain the above copyright
149687a165Smatt * notice, this list of conditions and the following disclaimer.
159687a165Smatt * 2. Redistributions in binary form must reproduce the above copyright
169687a165Smatt * notice, this list of conditions and the following disclaimer in the
179687a165Smatt * documentation and/or other materials provided with the distribution.
189687a165Smatt *
199687a165Smatt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209687a165Smatt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219687a165Smatt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229687a165Smatt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239687a165Smatt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249687a165Smatt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259687a165Smatt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269687a165Smatt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279687a165Smatt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289687a165Smatt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299687a165Smatt * POSSIBILITY OF SUCH DAMAGE.
309687a165Smatt */
319687a165Smatt
329687a165Smatt #include <sys/cdefs.h>
33*75b842b8Sskrll __KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.3 2023/05/07 12:41:49 skrll Exp $");
349687a165Smatt
359687a165Smatt #include "opt_multiprocessor.h"
369687a165Smatt
379687a165Smatt #include <sys/param.h>
389687a165Smatt #include <sys/mutex.h>
399687a165Smatt #include <sys/condvar.h>
409687a165Smatt #include <sys/cpu.h>
419687a165Smatt #include <sys/proc.h>
429687a165Smatt #include <sys/lwp.h>
439687a165Smatt #include <sys/pcu.h>
449687a165Smatt
459687a165Smatt #include <riscv/locore.h>
46*75b842b8Sskrll #include <riscv/frame.h>
479687a165Smatt
489687a165Smatt static void fpu_state_save(lwp_t *);
499687a165Smatt static void fpu_state_load(lwp_t *, u_int);
509687a165Smatt static void fpu_state_release(lwp_t *);
519687a165Smatt
529687a165Smatt const pcu_ops_t pcu_fpu_ops = {
539687a165Smatt .pcu_id = PCU_FPU,
549687a165Smatt .pcu_state_save = fpu_state_save,
559687a165Smatt .pcu_state_load = fpu_state_load,
569687a165Smatt .pcu_state_release = fpu_state_release
579687a165Smatt };
589687a165Smatt
59*75b842b8Sskrll static void
fpu_disable(void)60*75b842b8Sskrll fpu_disable(void)
61*75b842b8Sskrll {
62*75b842b8Sskrll csr_sstatus_clear(SR_FS);
63*75b842b8Sskrll }
64*75b842b8Sskrll
65*75b842b8Sskrll static void
fpu_enable(void)66*75b842b8Sskrll fpu_enable(void)
67*75b842b8Sskrll {
68*75b842b8Sskrll csr_sstatus_clear(SR_FS);
69*75b842b8Sskrll csr_sstatus_set(__SHIFTIN(SR_FS_CLEAN, SR_FS));
70*75b842b8Sskrll }
71*75b842b8Sskrll
729687a165Smatt void
fpu_state_save(lwp_t * l)739687a165Smatt fpu_state_save(lwp_t *l)
749687a165Smatt {
75*75b842b8Sskrll // struct trapframe * const tf = l->l_md.md_utf;
769687a165Smatt struct pcb * const pcb = lwp_getpcb(l);
779687a165Smatt struct fpreg * const fp = &pcb->pcb_fpregs;
789687a165Smatt
799687a165Smatt KASSERT(l->l_pcu_cpu[PCU_FPU] == curcpu());
809687a165Smatt
819687a165Smatt curcpu()->ci_ev_fpu_saves.ev_count++;
829687a165Smatt
839687a165Smatt // Enable FPU to save FP state
84*75b842b8Sskrll fpu_enable();
859687a165Smatt
869687a165Smatt // Save FCSR
87*75b842b8Sskrll fp->r_fcsr = fcsr_read();
889687a165Smatt
899687a165Smatt // Save FP register values.
909687a165Smatt __asm( "fsd f0, (0*%1)(%0)"
919687a165Smatt "\n\t" "fsd f1, (1*%1)(%0)"
929687a165Smatt "\n\t" "fsd f2, (2*%1)(%0)"
939687a165Smatt "\n\t" "fsd f3, (3*%1)(%0)"
949687a165Smatt "\n\t" "fsd f4, (4*%1)(%0)"
959687a165Smatt "\n\t" "fsd f5, (5*%1)(%0)"
969687a165Smatt "\n\t" "fsd f6, (6*%1)(%0)"
979687a165Smatt "\n\t" "fsd f7, (7*%1)(%0)"
989687a165Smatt "\n\t" "fsd f8, (8*%1)(%0)"
999687a165Smatt "\n\t" "fsd f9, (9*%1)(%0)"
1009687a165Smatt "\n\t" "fsd f10, (10*%1)(%0)"
1019687a165Smatt "\n\t" "fsd f11, (11*%1)(%0)"
1029687a165Smatt "\n\t" "fsd f12, (12*%1)(%0)"
1039687a165Smatt "\n\t" "fsd f13, (13*%1)(%0)"
1049687a165Smatt "\n\t" "fsd f14, (14*%1)(%0)"
1059687a165Smatt "\n\t" "fsd f15, (15*%1)(%0)"
1069687a165Smatt "\n\t" "fsd f16, (16*%1)(%0)"
1079687a165Smatt "\n\t" "fsd f17, (17*%1)(%0)"
1089687a165Smatt "\n\t" "fsd f18, (18*%1)(%0)"
1099687a165Smatt "\n\t" "fsd f19, (19*%1)(%0)"
1109687a165Smatt "\n\t" "fsd f20, (20*%1)(%0)"
1119687a165Smatt "\n\t" "fsd f21, (21*%1)(%0)"
1129687a165Smatt "\n\t" "fsd f22, (22*%1)(%0)"
1139687a165Smatt "\n\t" "fsd f23, (23*%1)(%0)"
1149687a165Smatt "\n\t" "fsd f24, (24*%1)(%0)"
1159687a165Smatt "\n\t" "fsd f25, (25*%1)(%0)"
1169687a165Smatt "\n\t" "fsd f26, (26*%1)(%0)"
1179687a165Smatt "\n\t" "fsd f27, (27*%1)(%0)"
1189687a165Smatt "\n\t" "fsd f28, (28*%1)(%0)"
1199687a165Smatt "\n\t" "fsd f29, (29*%1)(%0)"
1209687a165Smatt "\n\t" "fsd f30, (30*%1)(%0)"
1219687a165Smatt "\n\t" "fsd f31, (31*%1)(%0)"
1229687a165Smatt :: "r"(fp->r_fpreg),
123*75b842b8Sskrll "i"(sizeof(fp->r_fpreg[0])) : "memory");
1249687a165Smatt
1259687a165Smatt // Disable the FPU
126*75b842b8Sskrll fpu_disable();
1279687a165Smatt }
1289687a165Smatt
1299687a165Smatt void
fpu_state_load(lwp_t * l,u_int flags)1309687a165Smatt fpu_state_load(lwp_t *l, u_int flags)
1319687a165Smatt {
1329687a165Smatt struct trapframe * const tf = l->l_md.md_utf;
1339687a165Smatt struct pcb * const pcb = lwp_getpcb(l);
1349687a165Smatt struct fpreg * const fp = &pcb->pcb_fpregs;
1359687a165Smatt
136*75b842b8Sskrll KASSERT(l == curlwp);
137*75b842b8Sskrll // KASSERT(l->l_pcu_cpu[PCU_FPU] == curcpu());
1389687a165Smatt
1399687a165Smatt // If this is the first time the state is being loaded, zero it first.
1409687a165Smatt if (__predict_false((flags & PCU_VALID) == 0)) {
1419687a165Smatt memset(fp, 0, sizeof(*fp));
1429687a165Smatt }
1439687a165Smatt
1449687a165Smatt // Enable the FP when this lwp return to userspace.
145*75b842b8Sskrll tf->tf_sr &= ~SR_FS;
146*75b842b8Sskrll tf->tf_sr |= __SHIFTIN(SR_FS_CLEAN, SR_FS);
1479687a165Smatt
148d42188d8Sandvar // If this is a simple reenable, set the FPU enable flag and return
1499687a165Smatt if (flags & PCU_REENABLE) {
1509687a165Smatt curcpu()->ci_ev_fpu_reenables.ev_count++;
1519687a165Smatt return;
1529687a165Smatt }
1539687a165Smatt
1549687a165Smatt curcpu()->ci_ev_fpu_loads.ev_count++;
1559687a165Smatt
1569687a165Smatt
1579687a165Smatt // Enabling to load FP state. Interrupts will remain on.
158*75b842b8Sskrll fpu_enable();
1599687a165Smatt
1609687a165Smatt // load FP registers and establish processes' FP context.
1619687a165Smatt __asm( "fld f0, (0*%1)(%0)"
1629687a165Smatt "\n\t" "fld f1, (1*%1)(%0)"
1639687a165Smatt "\n\t" "fld f2, (2*%1)(%0)"
1649687a165Smatt "\n\t" "fld f3, (3*%1)(%0)"
1659687a165Smatt "\n\t" "fld f4, (4*%1)(%0)"
1669687a165Smatt "\n\t" "fld f5, (5*%1)(%0)"
1679687a165Smatt "\n\t" "fld f6, (6*%1)(%0)"
1689687a165Smatt "\n\t" "fld f7, (7*%1)(%0)"
1699687a165Smatt "\n\t" "fld f8, (8*%1)(%0)"
1709687a165Smatt "\n\t" "fld f9, (9*%1)(%0)"
1719687a165Smatt "\n\t" "fld f10, (10*%1)(%0)"
1729687a165Smatt "\n\t" "fld f11, (11*%1)(%0)"
1739687a165Smatt "\n\t" "fld f12, (12*%1)(%0)"
1749687a165Smatt "\n\t" "fld f13, (13*%1)(%0)"
1759687a165Smatt "\n\t" "fld f14, (14*%1)(%0)"
1769687a165Smatt "\n\t" "fld f15, (15*%1)(%0)"
1779687a165Smatt "\n\t" "fld f16, (16*%1)(%0)"
1789687a165Smatt "\n\t" "fld f17, (17*%1)(%0)"
1799687a165Smatt "\n\t" "fld f18, (18*%1)(%0)"
1809687a165Smatt "\n\t" "fld f19, (19*%1)(%0)"
1819687a165Smatt "\n\t" "fld f20, (20*%1)(%0)"
1829687a165Smatt "\n\t" "fld f21, (21*%1)(%0)"
1839687a165Smatt "\n\t" "fld f22, (22*%1)(%0)"
1849687a165Smatt "\n\t" "fld f23, (23*%1)(%0)"
1859687a165Smatt "\n\t" "fld f24, (24*%1)(%0)"
1869687a165Smatt "\n\t" "fld f25, (25*%1)(%0)"
1879687a165Smatt "\n\t" "fld f26, (26*%1)(%0)"
1889687a165Smatt "\n\t" "fld f27, (27*%1)(%0)"
1899687a165Smatt "\n\t" "fld f28, (28*%1)(%0)"
1909687a165Smatt "\n\t" "fld f29, (29*%1)(%0)"
1919687a165Smatt "\n\t" "fld f30, (30*%1)(%0)"
1929687a165Smatt "\n\t" "fld f31, (31*%1)(%0)"
1939687a165Smatt :: "r"(fp->r_fpreg),
194*75b842b8Sskrll "i"(sizeof(fp->r_fpreg[0])): "memory");
1959687a165Smatt
1969687a165Smatt // load FPCSR and disable FPU again
197*75b842b8Sskrll fcsr_write(fp->r_fcsr);
198*75b842b8Sskrll
199*75b842b8Sskrll fpu_disable();
2009687a165Smatt }
2019687a165Smatt
2029687a165Smatt void
fpu_state_release(lwp_t * l)2039687a165Smatt fpu_state_release(lwp_t *l)
2049687a165Smatt {
205*75b842b8Sskrll l->l_md.md_utf->tf_sr &= ~SR_FS;
206*75b842b8Sskrll fpu_disable();
2079687a165Smatt }
208