1/*-
2 * Copyright (c) 2009 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/lock.h>
34#include <sys/mutex.h>
35#include <sys/proc.h>
36
37#include <machine/frame.h>
38#include <machine/md_var.h>
39#include <machine/smp.h>
40
41static struct mtx ia64_highfp_mtx;
42
43static void
44ia64_highfp_init(void *_)
45{
46	mtx_init(&ia64_highfp_mtx, "High FP lock", NULL, MTX_SPIN);
47}
48SYSINIT(ia64_highfp_init, SI_SUB_LOCK, SI_ORDER_ANY, ia64_highfp_init, NULL);
49
50#ifdef SMP
51static int
52ia64_highfp_ipi(struct pcpu *cpu)
53{
54	int error;
55
56	ipi_send(cpu, ia64_ipi_highfp);
57	error = msleep_spin(&cpu->pc_fpcurthread, &ia64_highfp_mtx,
58	    "High FP", 0);
59	return (error);
60}
61#endif
62
63int
64ia64_highfp_drop(struct thread *td)
65{
66	struct pcb *pcb;
67	struct pcpu *cpu;
68
69	pcb = td->td_pcb;
70
71	mtx_lock_spin(&ia64_highfp_mtx);
72	cpu = pcb->pcb_fpcpu;
73	if (cpu != NULL) {
74		KASSERT(cpu->pc_fpcurthread == td,
75		    ("cpu->pc_fpcurthread != td"));
76		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
77		pcb->pcb_fpcpu = NULL;
78		cpu->pc_fpcurthread = NULL;
79	}
80	mtx_unlock_spin(&ia64_highfp_mtx);
81
82	return ((cpu != NULL) ? 1 : 0);
83}
84
85int
86ia64_highfp_enable(struct thread *td, struct trapframe *tf)
87{
88	struct pcb *pcb;
89	struct pcpu *cpu;
90	struct thread *td1;
91
92	pcb = td->td_pcb;
93
94	mtx_lock_spin(&ia64_highfp_mtx);
95	cpu = pcb->pcb_fpcpu;
96#ifdef SMP
97	if (cpu != NULL && cpu != pcpup) {
98		KASSERT(cpu->pc_fpcurthread == td,
99		    ("cpu->pc_fpcurthread != td"));
100		ia64_highfp_ipi(cpu);
101	}
102#endif
103	td1 = PCPU_GET(fpcurthread);
104	if (td1 != NULL && td1 != td) {
105		KASSERT(td1->td_pcb->pcb_fpcpu == pcpup,
106		    ("td1->td_pcb->pcb_fpcpu != pcpup"));
107		save_high_fp(&td1->td_pcb->pcb_high_fp);
108		td1->td_frame->tf_special.psr |= IA64_PSR_DFH;
109		td1->td_pcb->pcb_fpcpu = NULL;
110		PCPU_SET(fpcurthread, NULL);
111		td1 = NULL;
112	}
113	if (td1 == NULL) {
114		KASSERT(pcb->pcb_fpcpu == NULL, ("pcb->pcb_fpcpu != NULL"));
115		KASSERT(PCPU_GET(fpcurthread) == NULL,
116		    ("PCPU_GET(fpcurthread) != NULL"));
117		restore_high_fp(&pcb->pcb_high_fp);
118		PCPU_SET(fpcurthread, td);
119		pcb->pcb_fpcpu = pcpup;
120		tf->tf_special.psr &= ~IA64_PSR_MFH;
121	}
122	tf->tf_special.psr &= ~IA64_PSR_DFH;
123	mtx_unlock_spin(&ia64_highfp_mtx);
124
125	return ((td1 != NULL) ? 1 : 0);
126}
127
128int
129ia64_highfp_save(struct thread *td)
130{
131	struct pcb *pcb;
132	struct pcpu *cpu;
133
134	pcb = td->td_pcb;
135
136	mtx_lock_spin(&ia64_highfp_mtx);
137	cpu = pcb->pcb_fpcpu;
138#ifdef SMP
139	if (cpu != NULL && cpu != pcpup) {
140		KASSERT(cpu->pc_fpcurthread == td,
141		    ("cpu->pc_fpcurthread != td"));
142		ia64_highfp_ipi(cpu);
143	} else
144#endif
145	if (cpu != NULL) {
146		KASSERT(cpu->pc_fpcurthread == td,
147		    ("cpu->pc_fpcurthread != td"));
148		save_high_fp(&pcb->pcb_high_fp);
149		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
150		pcb->pcb_fpcpu = NULL;
151		cpu->pc_fpcurthread = NULL;
152	}
153	mtx_unlock_spin(&ia64_highfp_mtx);
154
155	return ((cpu != NULL) ? 1 : 0);
156}
157
158#ifdef SMP
159int
160ia64_highfp_save_ipi(void)
161{
162	struct thread *td;
163
164	mtx_lock_spin(&ia64_highfp_mtx);
165	td = PCPU_GET(fpcurthread);
166	if (td != NULL) {
167		KASSERT(td->td_pcb->pcb_fpcpu == pcpup,
168		    ("td->td_pcb->pcb_fpcpu != pcpup"));
169		save_high_fp(&td->td_pcb->pcb_high_fp);
170		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
171		td->td_pcb->pcb_fpcpu = NULL;
172		PCPU_SET(fpcurthread, NULL);
173	}
174	mtx_unlock_spin(&ia64_highfp_mtx);
175	wakeup(&PCPU_GET(fpcurthread));
176
177	return ((td != NULL) ? 1 : 0);
178}
179#endif
180