1116708Smarcel/*
2161802Smarcel * Copyright (c) 2003-2006 Marcel Moolenaar
3116708Smarcel * All rights reserved.
4116708Smarcel *
5116708Smarcel * Redistribution and use in source and binary forms, with or without
6116708Smarcel * modification, are permitted provided that the following conditions
7116708Smarcel * are met:
8116708Smarcel *
9116708Smarcel * 1. Redistributions of source code must retain the above copyright
10116708Smarcel *    notice, this list of conditions and the following disclaimer.
11116708Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12116708Smarcel *    notice, this list of conditions and the following disclaimer in the
13116708Smarcel *    documentation and/or other materials provided with the distribution.
14116708Smarcel *
15116708Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16116708Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17116708Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18116708Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19116708Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20116708Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21116708Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22116708Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23116708Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24116708Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25116708Smarcel *
26116708Smarcel * $FreeBSD$
27116708Smarcel */
28116708Smarcel
29116708Smarcel#ifndef _PTHREAD_MD_H_
30116708Smarcel#define	_PTHREAD_MD_H_
31116708Smarcel
32118510Sdeischen#include <sys/kse.h>
33118518Smarcel#include <stddef.h>
34118510Sdeischen#include <ucontext.h>
35118510Sdeischen
36120263Smarcel#define	KSE_STACKSIZE		16384
37161802Smarcel#define	DTV_OFFSET		offsetof(struct tcb, tcb_tp.tp_dtv)
38120263Smarcel
39118507Smarcel#define	THR_GETCONTEXT(ucp)	_ia64_save_context(&(ucp)->uc_mcontext)
40120254Smarcel#define	THR_SETCONTEXT(ucp)	PANIC("THR_SETCONTEXT() now in use!\n")
41116708Smarcel
42118510Sdeischen#define	PER_THREAD
43116708Smarcel
44118510Sdeischenstruct kcb;
45118510Sdeischenstruct kse;
46118510Sdeischenstruct pthread;
47118510Sdeischenstruct tcb;
48118510Sdeischen
49118510Sdeischen/*
50161841Smarcel * tp points to one of these. We define the TLS structure as a union
51161841Smarcel * containing a long double to enforce 16-byte alignment. This makes
52161841Smarcel * sure that there will not be any padding in struct tcb after the
53161841Smarcel * TLS structure.
54118510Sdeischen */
55161841Smarcelunion ia64_tp {
56161841Smarcel	void			*tp_dtv;
57161841Smarcel	long double		_align_;
58116771Smarcel};
59116771Smarcel
60118510Sdeischenstruct tcb {
61118510Sdeischen	struct kse_thr_mailbox	tcb_tmbx;
62118510Sdeischen	struct pthread		*tcb_thread;
63118510Sdeischen	struct kcb		*tcb_curkcb;
64118510Sdeischen	long			tcb_isfake;
65161841Smarcel	union ia64_tp		tcb_tp;
66118510Sdeischen};
67118510Sdeischen
68118510Sdeischenstruct kcb {
69118510Sdeischen	struct kse_mailbox	kcb_kmbx;
70161802Smarcel	struct kse		*kcb_kse;
71161802Smarcel	struct tcb		*kcb_curtcb;
72118510Sdeischen	struct tcb		kcb_faketcb;
73118510Sdeischen};
74118510Sdeischen
75161841Smarcelstatic __inline struct tcb *
76174127Srwatsonia64_get_tcb(void)
77161841Smarcel{
78161841Smarcel	register char *tp __asm("%r13");
79118510Sdeischen
80161841Smarcel	return ((struct tcb *)(tp - offsetof(struct tcb, tcb_tp)));
81161841Smarcel}
82118518Smarcel
83161841Smarcelstatic __inline void
84161841Smarcelia64_set_tcb(struct tcb *tcb)
85161841Smarcel{
86161841Smarcel	register char *tp __asm("%r13");
87161841Smarcel
88161841Smarcel	__asm __volatile("mov %0 = %1;;" : "=r"(tp) : "r"(&tcb->tcb_tp));
89161841Smarcel}
90161841Smarcel
91118510Sdeischen/*
92118510Sdeischen * The kcb and tcb constructors.
93118510Sdeischen */
94133756Sdfrstruct tcb	*_tcb_ctor(struct pthread *, int);
95118510Sdeischenvoid		_tcb_dtor(struct tcb *);
96118510Sdeischenstruct kcb	*_kcb_ctor(struct kse *kse);
97118510Sdeischenvoid		_kcb_dtor(struct kcb *);
98118510Sdeischen
99118510Sdeischen/* Called from the KSE to set its private data. */
100118510Sdeischenstatic __inline void
101118510Sdeischen_kcb_set(struct kcb *kcb)
102118510Sdeischen{
103118510Sdeischen	/* There is no thread yet; use the fake tcb. */
104161841Smarcel	ia64_set_tcb(&kcb->kcb_faketcb);
105118510Sdeischen}
106118510Sdeischen
107118510Sdeischen/*
108118510Sdeischen * Get the current kcb.
109118510Sdeischen *
110118510Sdeischen * This can only be called while in a critical region; don't
111118510Sdeischen * worry about having the kcb changed out from under us.
112118510Sdeischen */
113118510Sdeischenstatic __inline struct kcb *
114118510Sdeischen_kcb_get(void)
115118510Sdeischen{
116161841Smarcel	return (ia64_get_tcb()->tcb_curkcb);
117118510Sdeischen}
118118510Sdeischen
119118510Sdeischen/*
120118510Sdeischen * Enter a critical region.
121118510Sdeischen *
122118510Sdeischen * Read and clear km_curthread in the kse mailbox.
123118510Sdeischen */
124118510Sdeischenstatic __inline struct kse_thr_mailbox *
125118510Sdeischen_kcb_critical_enter(void)
126118510Sdeischen{
127161841Smarcel	struct tcb *tcb;
128118510Sdeischen	struct kse_thr_mailbox *crit;
129118510Sdeischen	uint32_t flags;
130118510Sdeischen
131161841Smarcel	tcb = ia64_get_tcb();
132161841Smarcel	if (tcb->tcb_isfake != 0) {
133118510Sdeischen		/*
134118510Sdeischen		 * We already are in a critical region since
135118510Sdeischen		 * there is no current thread.
136118510Sdeischen		 */
137118510Sdeischen		crit = NULL;
138118510Sdeischen	} else {
139161841Smarcel		flags = tcb->tcb_tmbx.tm_flags;
140161841Smarcel		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
141161841Smarcel		crit = tcb->tcb_curkcb->kcb_kmbx.km_curthread;
142161841Smarcel		tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL;
143161841Smarcel		tcb->tcb_tmbx.tm_flags = flags;
144118510Sdeischen	}
145118510Sdeischen	return (crit);
146118510Sdeischen}
147118510Sdeischen
148118510Sdeischenstatic __inline void
149118510Sdeischen_kcb_critical_leave(struct kse_thr_mailbox *crit)
150118510Sdeischen{
151161841Smarcel	struct tcb *tcb;
152161841Smarcel
153161841Smarcel	tcb = ia64_get_tcb();
154118510Sdeischen	/* No need to do anything if this is a fake tcb. */
155161841Smarcel	if (tcb->tcb_isfake == 0)
156161841Smarcel		tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit;
157118510Sdeischen}
158118510Sdeischen
159118510Sdeischenstatic __inline int
160118510Sdeischen_kcb_in_critical(void)
161118510Sdeischen{
162161841Smarcel	struct tcb *tcb;
163118510Sdeischen	uint32_t flags;
164118510Sdeischen	int ret;
165118510Sdeischen
166161841Smarcel	tcb = ia64_get_tcb();
167161841Smarcel	if (tcb->tcb_isfake != 0) {
168118510Sdeischen		/*
169118510Sdeischen		 * We are in a critical region since there is no
170118510Sdeischen		 * current thread.
171118510Sdeischen		 */
172118510Sdeischen		ret = 1;
173118510Sdeischen	} else {
174161841Smarcel		flags = tcb->tcb_tmbx.tm_flags;
175161841Smarcel		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
176161841Smarcel		ret = (tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL);
177161841Smarcel		tcb->tcb_tmbx.tm_flags = flags;
178118510Sdeischen	}
179118510Sdeischen	return (ret);
180118510Sdeischen}
181118510Sdeischen
182118510Sdeischenstatic __inline void
183118510Sdeischen_tcb_set(struct kcb *kcb, struct tcb *tcb)
184118510Sdeischen{
185118518Smarcel	if (tcb == NULL)
186118518Smarcel		tcb = &kcb->kcb_faketcb;
187118518Smarcel	kcb->kcb_curtcb = tcb;
188118518Smarcel	tcb->tcb_curkcb = kcb;
189161841Smarcel	ia64_set_tcb(tcb);
190118510Sdeischen}
191118510Sdeischen
192118510Sdeischenstatic __inline struct tcb *
193118510Sdeischen_tcb_get(void)
194118510Sdeischen{
195161841Smarcel	return (ia64_get_tcb());
196118510Sdeischen}
197118510Sdeischen
198118510Sdeischenstatic __inline struct pthread *
199118510Sdeischen_get_curthread(void)
200118510Sdeischen{
201161841Smarcel	return (ia64_get_tcb()->tcb_thread);
202118510Sdeischen}
203118510Sdeischen
204118510Sdeischen/*
205118510Sdeischen * Get the current kse.
206118510Sdeischen *
207118519Sdeischen * Like _kcb_get(), this can only be called while in a critical region.
208118510Sdeischen */
209118510Sdeischenstatic __inline struct kse *
210118510Sdeischen_get_curkse(void)
211118510Sdeischen{
212161841Smarcel	return (ia64_get_tcb()->tcb_curkcb->kcb_kse);
213118510Sdeischen}
214118510Sdeischen
215120254Smarcelvoid _ia64_break_setcontext(mcontext_t *mc);
216116866Smarcelvoid _ia64_enter_uts(kse_func_t uts, struct kse_mailbox *km, void *stack,
217116866Smarcel    size_t stacksz);
218116866Smarcelint _ia64_restore_context(mcontext_t *mc, intptr_t val, intptr_t *loc);
219116866Smarcelint _ia64_save_context(mcontext_t *mc);
220116866Smarcel
221116866Smarcelstatic __inline int
222118510Sdeischen_thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
223116866Smarcel{
224118510Sdeischen	if (_ia64_save_context(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
225118510Sdeischen		/* Make the fake tcb the current thread. */
226118510Sdeischen		kcb->kcb_curtcb = &kcb->kcb_faketcb;
227161841Smarcel		ia64_set_tcb(&kcb->kcb_faketcb);
228118510Sdeischen		_ia64_enter_uts(kcb->kcb_kmbx.km_func, &kcb->kcb_kmbx,
229118510Sdeischen		    kcb->kcb_kmbx.km_stack.ss_sp,
230118510Sdeischen		    kcb->kcb_kmbx.km_stack.ss_size);
231116866Smarcel		/* We should not reach here. */
232116866Smarcel		return (-1);
233116866Smarcel	}
234116866Smarcel	return (0);
235116866Smarcel}
236116866Smarcel
237116866Smarcelstatic __inline int
238118510Sdeischen_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox)
239116866Smarcel{
240118592Smarcel	mcontext_t *mc;
241118592Smarcel
242118518Smarcel	_tcb_set(kcb, tcb);
243118592Smarcel	mc = &tcb->tcb_tmbx.tm_context.uc_mcontext;
244118592Smarcel	if (mc->mc_flags & _MC_FLAGS_ASYNC_CONTEXT) {
245118592Smarcel		if (setmbox) {
246118592Smarcel			mc->mc_flags |= _MC_FLAGS_KSE_SET_MBOX;
247118592Smarcel			mc->mc_special.ifa =
248118592Smarcel			    (intptr_t)&kcb->kcb_kmbx.km_curthread;
249118592Smarcel			mc->mc_special.isr = (intptr_t)&tcb->tcb_tmbx;
250118592Smarcel		}
251120254Smarcel		_ia64_break_setcontext(mc);
252123255Smarcel	} else if (mc->mc_flags & _MC_FLAGS_SYSCALL_CONTEXT) {
253123255Smarcel		if (setmbox)
254132021Sdavidxu			kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX);
255123255Smarcel		else
256132021Sdavidxu			kse_switchin(&tcb->tcb_tmbx, 0);
257118592Smarcel	} else {
258118592Smarcel		if (setmbox)
259118592Smarcel			_ia64_restore_context(mc, (intptr_t)&tcb->tcb_tmbx,
260118592Smarcel			    (intptr_t *)&kcb->kcb_kmbx.km_curthread);
261118592Smarcel		else
262118592Smarcel			_ia64_restore_context(mc, 0, NULL);
263118592Smarcel	}
264116866Smarcel	/* We should not reach here. */
265116866Smarcel	return (-1);
266116866Smarcel}
267116866Smarcel
268116708Smarcel#endif /* _PTHREAD_MD_H_ */
269