1132332Smarcel/*
2132332Smarcel * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
3132332Smarcel * All rights reserved.
4132332Smarcel *
5132332Smarcel * Redistribution and use in source and binary forms, with or without
6132332Smarcel * modification, are permitted provided that the following conditions
7132332Smarcel * are met:
8132332Smarcel * 1. Redistributions of source code must retain the above copyright
9132332Smarcel *    notice, this list of conditions and the following disclaimer.
10132332Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11132332Smarcel *    notice, this list of conditions and the following disclaimer in the
12132332Smarcel *    documentation and/or other materials provided with the distribution.
13132332Smarcel *
14132332Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132332Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132332Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132332Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132332Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19132332Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20132332Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132332Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22132332Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23132332Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24132332Smarcel * SUCH DAMAGE.
25132332Smarcel */
26132332Smarcel
27132332Smarcel#include <sys/cdefs.h>
28132332Smarcel__FBSDID("$FreeBSD$");
29132332Smarcel
30132332Smarcel#include <stddef.h>
31132332Smarcel#include <stdlib.h>
32132332Smarcel#include <string.h>
33132332Smarcel#include <unistd.h>
34132332Smarcel#include <pthread.h>
35132332Smarcel#include <sys/types.h>
36177490Sdavidxu#include <sys/linker_set.h>
37132332Smarcel#include <sys/ptrace.h>
38132332Smarcel#include <proc_service.h>
39132332Smarcel#include <thread_db.h>
40132332Smarcel
41132332Smarcel#include "libpthread_db.h"
42177526Sjeff#include "kse.h"
43132332Smarcel
44132332Smarcel#define P2T(c) ps2td(c)
45132332Smarcel
46132332Smarcelstatic void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
47132332Smarcelstatic int pt_validate(const td_thrhandle_t *th);
48132332Smarcel
49132332Smarcelstatic int
50132332Smarcelps2td(int c)
51132332Smarcel{
52132332Smarcel	switch (c) {
53132332Smarcel	case PS_OK:
54132332Smarcel		return TD_OK;
55132332Smarcel	case PS_ERR:
56132332Smarcel		return TD_ERR;
57132332Smarcel	case PS_BADPID:
58132332Smarcel		return TD_BADPH;
59132332Smarcel	case PS_BADLID:
60132332Smarcel		return TD_NOLWP;
61132332Smarcel	case PS_BADADDR:
62132332Smarcel		return TD_ERR;
63132332Smarcel	case PS_NOSYM:
64132332Smarcel		return TD_NOLIBTHREAD;
65132332Smarcel	case PS_NOFREGS:
66132332Smarcel		return TD_NOFPREGS;
67132332Smarcel	default:
68132332Smarcel		return TD_ERR;
69132332Smarcel	}
70132332Smarcel}
71132332Smarcel
72132332Smarcelstatic long
73181059Smarcelpt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, enum pt_type type)
74132332Smarcel{
75132332Smarcel	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
76132332Smarcel	struct pt_map *new;
77132332Smarcel	int i, first = -1;
78132332Smarcel
79132332Smarcel	/* leave zero out */
80132332Smarcel	for (i = 1; i < ta->map_len; ++i) {
81132332Smarcel		if (ta->map[i].type == PT_NONE) {
82132332Smarcel			if (first == -1)
83132332Smarcel				first = i;
84132332Smarcel		} else if (ta->map[i].type == type && ta->map[i].thr == pt) {
85132332Smarcel				return (i);
86132332Smarcel		}
87132332Smarcel	}
88132332Smarcel
89132332Smarcel	if (first == -1) {
90132332Smarcel		if (ta->map_len == 0) {
91132332Smarcel			ta->map = calloc(20, sizeof(struct pt_map));
92132332Smarcel			if (ta->map == NULL)
93132332Smarcel				return (-1);
94132332Smarcel			ta->map_len = 20;
95132332Smarcel			first = 1;
96132332Smarcel		} else {
97132332Smarcel			new = realloc(ta->map,
98132332Smarcel			              sizeof(struct pt_map) * ta->map_len * 2);
99132332Smarcel			if (new == NULL)
100132332Smarcel				return (-1);
101132332Smarcel			memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
102132332Smarcel			       ta->map_len);
103132332Smarcel			first = ta->map_len;
104132332Smarcel			ta->map = new;
105132332Smarcel			ta->map_len *= 2;
106132332Smarcel		}
107132332Smarcel	}
108132332Smarcel
109132332Smarcel	ta->map[first].type = type;
110132332Smarcel	ta->map[first].thr = pt;
111132332Smarcel	return (first);
112132332Smarcel}
113132332Smarcel
114132332Smarcelstatic td_err_e
115132332Smarcelpt_init(void)
116132332Smarcel{
117132332Smarcel	pt_md_init();
118132332Smarcel	return (0);
119132332Smarcel}
120132332Smarcel
121132332Smarcelstatic td_err_e
122132332Smarcelpt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
123132332Smarcel{
124132332Smarcel#define LOOKUP_SYM(proc, sym, addr) 			\
125132332Smarcel	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
126132332Smarcel	if (ret != 0) {					\
127132332Smarcel		TDBG("can not find symbol: %s\n", sym);	\
128132332Smarcel		ret = TD_NOLIBTHREAD;			\
129132332Smarcel		goto error;				\
130132332Smarcel	}
131132332Smarcel
132133802Sdavidxu#define	LOOKUP_VAL(proc, sym, val)			\
133133802Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
134133802Sdavidxu	if (ret != 0) {					\
135133802Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
136133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
137133802Sdavidxu		goto error;				\
138133802Sdavidxu	}						\
139133802Sdavidxu	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
140133802Sdavidxu	if (ret != 0) {					\
141133802Sdavidxu		TDBG("can not read value of %s\n", sym);\
142133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
143133802Sdavidxu		goto error;				\
144133802Sdavidxu	}
145133802Sdavidxu
146132332Smarcel	td_thragent_t *ta;
147133802Sdavidxu	psaddr_t vaddr;
148132332Smarcel	int dbg;
149132332Smarcel	int ret;
150132332Smarcel
151132332Smarcel	TDBG_FUNC();
152132332Smarcel
153132332Smarcel	ta = malloc(sizeof(td_thragent_t));
154132332Smarcel	if (ta == NULL)
155132332Smarcel		return (TD_MALLOC);
156132332Smarcel
157132332Smarcel	ta->ph = ph;
158132332Smarcel	ta->thread_activated = 0;
159132332Smarcel	ta->map = NULL;
160132332Smarcel	ta->map_len = 0;
161132332Smarcel
162132332Smarcel	LOOKUP_SYM(ph, "_libkse_debug",		&ta->libkse_debug_addr);
163132332Smarcel	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
164132332Smarcel	LOOKUP_SYM(ph, "_thread_activated",	&ta->thread_activated_addr);
165132332Smarcel	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
166132332Smarcel	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
167133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
168133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse_locklevel", &ta->thread_off_kse_locklevel);
169133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse",	&ta->thread_off_kse);
170133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
171133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
172133802Sdavidxu	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
173133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
174133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
175133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tmbx",	&ta->thread_off_tmbx);
176133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_thr_locklevel",	&ta->thread_off_thr_locklevel);
177133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
178133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
179133802Sdavidxu	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
180133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
181133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
182133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
183133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
184158680Sdavidxu	LOOKUP_VAL(ph, "_thread_off_sigmask",	&ta->thread_off_sigmask);
185158680Sdavidxu	LOOKUP_VAL(ph, "_thread_off_sigpend",	&ta->thread_off_sigpend);
186132332Smarcel	dbg = getpid();
187132332Smarcel	/*
188132332Smarcel	 * If this fails it probably means we're debugging a core file and
189132332Smarcel	 * can't write to it.
190132332Smarcel	 */
191132332Smarcel	ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
192132332Smarcel	*pta = ta;
193132332Smarcel	return (0);
194132332Smarcel
195132332Smarcelerror:
196132332Smarcel	free(ta);
197132332Smarcel	return (ret);
198132332Smarcel}
199132332Smarcel
200132332Smarcelstatic td_err_e
201132332Smarcelpt_ta_delete(td_thragent_t *ta)
202132332Smarcel{
203132332Smarcel	int dbg;
204132332Smarcel
205132332Smarcel	TDBG_FUNC();
206132332Smarcel
207132332Smarcel	dbg = 0;
208132332Smarcel	/*
209132332Smarcel	 * Error returns from this write are not really a problem;
210132332Smarcel	 * the process doesn't exist any more.
211132332Smarcel	 */
212132332Smarcel	ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
213132332Smarcel	if (ta->map)
214132332Smarcel		free(ta->map);
215132332Smarcel	free(ta);
216132332Smarcel	return (TD_OK);
217132332Smarcel}
218132332Smarcel
219132332Smarcelstatic td_err_e
220132332Smarcelpt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
221132332Smarcel{
222132332Smarcel	prgregset_t gregs;
223132332Smarcel	psaddr_t pt, tcb_addr;
224132332Smarcel	lwpid_t lwp;
225132332Smarcel	int ret;
226132332Smarcel
227132332Smarcel	TDBG_FUNC();
228132332Smarcel
229132332Smarcel	if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
230132332Smarcel		return (TD_NOTHR);
231183021Smarcel
232183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
233132332Smarcel	if (ret != 0)
234183021Smarcel		return (TD_ERR);
235132332Smarcel	if (ta->map[id].type == PT_LWP) {
236132332Smarcel		/*
237132332Smarcel		 * if we are referencing a lwp, make sure it was not already
238132332Smarcel		 * mapped to user thread.
239132332Smarcel		 */
240132332Smarcel		while (pt != 0) {
241183021Smarcel			ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb,
242183021Smarcel			    &tcb_addr);
243132332Smarcel			if (ret != 0)
244183021Smarcel				return (TD_ERR);
245183021Smarcel			ret = thr_pread_int(ta, tcb_addr + ta->thread_off_tmbx +
246183021Smarcel			    offsetof(struct kse_thr_mailbox, tm_lwp), &lwp);
247132332Smarcel			if (ret != 0)
248183021Smarcel				return (TD_ERR);
249132332Smarcel			/*
250132332Smarcel			 * If the lwp was already mapped to userland thread,
251132332Smarcel			 * we shouldn't reference it directly in future.
252132332Smarcel			 */
253132332Smarcel			if (lwp == ta->map[id].lwp) {
254132332Smarcel				ta->map[id].type = PT_NONE;
255132332Smarcel				return (TD_NOTHR);
256132332Smarcel			}
257132332Smarcel			/* get next thread */
258183021Smarcel			ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
259132332Smarcel			if (ret != 0)
260183021Smarcel				return (TD_ERR);
261132332Smarcel		}
262132332Smarcel		/* check lwp */
263155411Sdavidxu		ret = ps_lgetregs(ta->ph, ta->map[id].lwp, gregs);
264155411Sdavidxu		if (ret != PS_OK) {
265132332Smarcel			/* no longer exists */
266132332Smarcel			ta->map[id].type = PT_NONE;
267132332Smarcel			return (TD_NOTHR);
268132332Smarcel		}
269132332Smarcel	} else {
270132332Smarcel		while (pt != 0 && ta->map[id].thr != pt) {
271183021Smarcel			ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb,
272183021Smarcel			    &tcb_addr);
273132332Smarcel			if (ret != 0)
274183021Smarcel				return (TD_ERR);
275132332Smarcel			/* get next thread */
276183021Smarcel			ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
277132332Smarcel			if (ret != 0)
278183021Smarcel				return (TD_ERR);
279132332Smarcel		}
280132332Smarcel
281132332Smarcel		if (pt == 0) {
282132332Smarcel			/* no longer exists */
283132332Smarcel			ta->map[id].type = PT_NONE;
284132332Smarcel			return (TD_NOTHR);
285132332Smarcel		}
286132332Smarcel	}
287132332Smarcel	th->th_ta = ta;
288132332Smarcel	th->th_tid = id;
289144663Sdavidxu	th->th_thread = pt;
290132332Smarcel	return (TD_OK);
291132332Smarcel}
292132332Smarcel
293132332Smarcelstatic td_err_e
294132332Smarcelpt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
295132332Smarcel{
296183021Smarcel	psaddr_t pt, tcb_addr;
297183021Smarcel	lwpid_t lwp1;
298132332Smarcel	int ret;
299183021Smarcel
300132332Smarcel	TDBG_FUNC();
301132332Smarcel
302183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
303132332Smarcel	if (ret != 0)
304183021Smarcel		return (TD_ERR);
305132332Smarcel	while (pt != 0) {
306183021Smarcel		ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb, &tcb_addr);
307132332Smarcel		if (ret != 0)
308183021Smarcel			return (TD_ERR);
309183021Smarcel		ret = thr_pread_int(ta, tcb_addr + ta->thread_off_tmbx +
310183021Smarcel		    offsetof(struct kse_thr_mailbox, tm_lwp), &lwp1);
311132332Smarcel		if (ret != 0)
312183021Smarcel			return (TD_ERR);
313183021Smarcel		if (lwp1 == lwp) {
314132332Smarcel			th->th_ta = ta;
315132332Smarcel			th->th_tid = pt_map_thread(ta, pt, PT_USER);
316132332Smarcel			if (th->th_tid == -1)
317132332Smarcel				return (TD_MALLOC);
318132332Smarcel			pt_unmap_lwp(ta, lwp);
319144663Sdavidxu			th->th_thread = pt;
320132332Smarcel			return (TD_OK);
321132332Smarcel		}
322132332Smarcel
323132332Smarcel		/* get next thread */
324183021Smarcel		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
325132332Smarcel		if (ret != 0)
326183021Smarcel			return (TD_ERR);
327132332Smarcel	}
328132332Smarcel
329132332Smarcel	return (TD_NOTHR);
330132332Smarcel}
331132332Smarcel
332132332Smarcelstatic td_err_e
333181341Smarcelpt_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
334181341Smarcel    void *cbdata_p, td_thr_state_e state __unused, int ti_pri __unused,
335181341Smarcel    sigset_t *ti_sigmask_p __unused, unsigned int ti_user_flags __unused)
336132332Smarcel{
337132332Smarcel	td_thrhandle_t th;
338132332Smarcel	psaddr_t pt;
339132332Smarcel	ps_err_e pserr;
340183021Smarcel	int activated, ret;
341132332Smarcel
342132332Smarcel	TDBG_FUNC();
343132332Smarcel
344132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
345132332Smarcel	    sizeof(int));
346132332Smarcel	if (pserr != PS_OK)
347132332Smarcel		return (P2T(pserr));
348132332Smarcel	if (!activated)
349132332Smarcel		return (TD_OK);
350132332Smarcel
351183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
352183021Smarcel	if (ret != 0)
353183021Smarcel		return (TD_ERR);
354132332Smarcel	while (pt != 0) {
355132332Smarcel		th.th_ta = ta;
356132332Smarcel		th.th_tid = pt_map_thread(ta, pt, PT_USER);
357144663Sdavidxu		th.th_thread = pt;
358132332Smarcel		/* should we unmap lwp here ? */
359132332Smarcel		if (th.th_tid == -1)
360132332Smarcel			return (TD_MALLOC);
361132332Smarcel		if ((*callback)(&th, cbdata_p))
362132332Smarcel			return (TD_DBERR);
363132332Smarcel		/* get next thread */
364183021Smarcel		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
365183021Smarcel		if (ret != 0)
366183021Smarcel			return (TD_ERR);
367132332Smarcel	}
368132332Smarcel	return (TD_OK);
369132332Smarcel}
370132332Smarcel
371132332Smarcelstatic td_err_e
372132332Smarcelpt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
373132332Smarcel{
374181341Smarcel	void *keytable;
375133802Sdavidxu	void *destructor;
376133802Sdavidxu	int i, ret, allocated;
377132332Smarcel
378132332Smarcel	TDBG_FUNC();
379132332Smarcel
380133802Sdavidxu	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
381133802Sdavidxu	if (keytable == NULL)
382133802Sdavidxu		return (TD_MALLOC);
383132332Smarcel	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
384133802Sdavidxu	               ta->thread_max_keys * ta->thread_size_key);
385133805Sdavidxu	if (ret != 0) {
386133805Sdavidxu		free(keytable);
387132332Smarcel		return (P2T(ret));
388133805Sdavidxu	}
389133802Sdavidxu	for (i = 0; i < ta->thread_max_keys; i++) {
390181341Smarcel		allocated = *(int *)(void *)((uintptr_t)keytable +
391181341Smarcel		    i * ta->thread_size_key + ta->thread_off_key_allocated);
392181341Smarcel		destructor = *(void **)(void *)((uintptr_t)keytable +
393181341Smarcel		    i * ta->thread_size_key + ta->thread_off_key_destructor);
394133802Sdavidxu		if (allocated) {
395133802Sdavidxu			ret = (ki)(i, destructor, arg);
396133802Sdavidxu			if (ret != 0) {
397133802Sdavidxu				free(keytable);
398132332Smarcel				return (TD_DBERR);
399133802Sdavidxu			}
400132332Smarcel		}
401132332Smarcel	}
402133802Sdavidxu	free(keytable);
403132332Smarcel	return (TD_OK);
404132332Smarcel}
405132332Smarcel
406132332Smarcelstatic td_err_e
407181341Smarcelpt_ta_event_addr(const td_thragent_t *ta __unused, td_event_e event __unused,
408181341Smarcel    td_notify_t *ptr __unused)
409132332Smarcel{
410132332Smarcel	TDBG_FUNC();
411144922Sdavidxu	return (TD_ERR);
412132332Smarcel}
413132332Smarcel
414132332Smarcelstatic td_err_e
415181341Smarcelpt_ta_set_event(const td_thragent_t *ta __unused,
416181341Smarcel    td_thr_events_t *events __unused)
417132332Smarcel{
418132332Smarcel	TDBG_FUNC();
419144922Sdavidxu	return (0);
420132332Smarcel}
421132332Smarcel
422132332Smarcelstatic td_err_e
423181341Smarcelpt_ta_clear_event(const td_thragent_t *ta __unused,
424181341Smarcel    td_thr_events_t *events __unused)
425132332Smarcel{
426132332Smarcel	TDBG_FUNC();
427144922Sdavidxu	return (0);
428132332Smarcel}
429132332Smarcel
430132332Smarcelstatic td_err_e
431181341Smarcelpt_ta_event_getmsg(const td_thragent_t *ta __unused,
432181341Smarcel    td_event_msg_t *msg __unused)
433132332Smarcel{
434132332Smarcel	TDBG_FUNC();
435132332Smarcel	return (TD_NOMSG);
436132332Smarcel}
437132332Smarcel
438132332Smarcelstatic td_err_e
439132951Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
440132951Sdavidxu{
441181341Smarcel	const td_thragent_t *ta = th->th_ta;
442132951Sdavidxu	psaddr_t tcb_addr, tmbx_addr, ptr;
443132951Sdavidxu	lwpid_t lwp;
444132951Sdavidxu	uint32_t dflags;
445133342Sdavidxu	int attrflags, locklevel, ret;
446132951Sdavidxu
447132951Sdavidxu	TDBG_FUNC();
448132951Sdavidxu
449132951Sdavidxu	ret = pt_validate(th);
450132951Sdavidxu	if (ret)
451132951Sdavidxu		return (ret);
452132951Sdavidxu
453132951Sdavidxu	if (ta->map[th->th_tid].type == PT_LWP) {
454132951Sdavidxu		if (suspend)
455132951Sdavidxu			ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
456132951Sdavidxu		else
457132951Sdavidxu			ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
458132951Sdavidxu		return (P2T(ret));
459132951Sdavidxu	}
460132951Sdavidxu
461132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
462133802Sdavidxu		ta->thread_off_attr_flags,
463132951Sdavidxu		&attrflags, sizeof(attrflags));
464132951Sdavidxu	if (ret != 0)
465132951Sdavidxu		return (P2T(ret));
466132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
467133802Sdavidxu	               ta->thread_off_tcb,
468133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
469132951Sdavidxu	if (ret != 0)
470132951Sdavidxu		return (P2T(ret));
471133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
472132951Sdavidxu	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
473132951Sdavidxu	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
474132951Sdavidxu	if (ret != 0)
475132951Sdavidxu		return (P2T(ret));
476133342Sdavidxu
477133342Sdavidxu	if (lwp != 0) {
478133342Sdavidxu		/* don't suspend signal thread */
479133802Sdavidxu		if (attrflags & 0x200)
480133342Sdavidxu			return (0);
481133342Sdavidxu		if (attrflags & PTHREAD_SCOPE_SYSTEM) {
482133342Sdavidxu			/*
483133342Sdavidxu			 * don't suspend system scope thread if it is holding
484133342Sdavidxu			 * some low level locks
485133342Sdavidxu			 */
486133802Sdavidxu			ptr = ta->map[th->th_tid].thr + ta->thread_off_kse;
487133342Sdavidxu			ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
488133342Sdavidxu			if (ret != 0)
489133342Sdavidxu				return (P2T(ret));
490133802Sdavidxu			ret = ps_pread(ta->ph, ptr + ta->thread_off_kse_locklevel,
491133802Sdavidxu				&locklevel, sizeof(int));
492133342Sdavidxu			if (ret != 0)
493133342Sdavidxu				return (P2T(ret));
494133342Sdavidxu			if (locklevel <= 0) {
495133342Sdavidxu				ptr = ta->map[th->th_tid].thr +
496133802Sdavidxu					ta->thread_off_thr_locklevel;
497133342Sdavidxu				ret = ps_pread(ta->ph, ptr, &locklevel,
498133342Sdavidxu					sizeof(int));
499133342Sdavidxu				if (ret != 0)
500133342Sdavidxu					return (P2T(ret));
501133342Sdavidxu			}
502133342Sdavidxu			if (suspend) {
503133342Sdavidxu				if (locklevel <= 0)
504133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
505133342Sdavidxu			} else {
506132951Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
507133342Sdavidxu			}
508132951Sdavidxu			if (ret != 0)
509132951Sdavidxu				return (P2T(ret));
510133342Sdavidxu			/* FALLTHROUGH */
511133342Sdavidxu		} else {
512133342Sdavidxu			struct ptrace_lwpinfo pl;
513133342Sdavidxu
514155413Sdavidxu			if (ps_linfo(ta->ph, lwp, (caddr_t)&pl))
515133342Sdavidxu				return (TD_ERR);
516133342Sdavidxu			if (suspend) {
517133342Sdavidxu				if (!(pl.pl_flags & PL_FLAG_BOUND))
518133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
519133342Sdavidxu			} else {
520133342Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
521133342Sdavidxu			}
522133342Sdavidxu			if (ret != 0)
523133342Sdavidxu				return (P2T(ret));
524133342Sdavidxu			/* FALLTHROUGH */
525132951Sdavidxu		}
526132951Sdavidxu	}
527132951Sdavidxu	/* read tm_dflags */
528132951Sdavidxu	ret = ps_pread(ta->ph,
529132951Sdavidxu		tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
530132951Sdavidxu		&dflags, sizeof(dflags));
531132951Sdavidxu	if (ret != 0)
532132951Sdavidxu		return (P2T(ret));
533132951Sdavidxu	if (suspend)
534133047Sdavidxu		dflags |= TMDF_SUSPEND;
535132951Sdavidxu	else
536133047Sdavidxu		dflags &= ~TMDF_SUSPEND;
537132951Sdavidxu	ret = ps_pwrite(ta->ph,
538132951Sdavidxu	       tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
539132951Sdavidxu	       &dflags, sizeof(dflags));
540132951Sdavidxu	return (P2T(ret));
541132951Sdavidxu}
542132951Sdavidxu
543132951Sdavidxustatic td_err_e
544132332Smarcelpt_thr_dbresume(const td_thrhandle_t *th)
545132332Smarcel{
546132332Smarcel	TDBG_FUNC();
547132951Sdavidxu
548132951Sdavidxu	return pt_dbsuspend(th, 0);
549132332Smarcel}
550132332Smarcel
551132332Smarcelstatic td_err_e
552132332Smarcelpt_thr_dbsuspend(const td_thrhandle_t *th)
553132332Smarcel{
554132332Smarcel	TDBG_FUNC();
555132951Sdavidxu
556132951Sdavidxu	return pt_dbsuspend(th, 1);
557132332Smarcel}
558132332Smarcel
559132332Smarcelstatic td_err_e
560132332Smarcelpt_thr_validate(const td_thrhandle_t *th)
561132332Smarcel{
562132332Smarcel	td_thrhandle_t temp;
563132332Smarcel	int ret;
564132332Smarcel
565132332Smarcel	TDBG_FUNC();
566132332Smarcel
567132332Smarcel	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
568132332Smarcel	                       &temp);
569132951Sdavidxu	return (ret);
570132332Smarcel}
571132332Smarcel
572132332Smarcelstatic td_err_e
573209689Skibpt_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
574132332Smarcel{
575132332Smarcel	const td_thragent_t *ta = th->th_ta;
576158680Sdavidxu	struct ptrace_lwpinfo linfo;
577133802Sdavidxu	psaddr_t tcb_addr;
578133802Sdavidxu	uint32_t dflags;
579158680Sdavidxu	lwpid_t lwp;
580133802Sdavidxu	int state;
581132332Smarcel	int ret;
582158680Sdavidxu	int attrflags;
583132332Smarcel
584132332Smarcel	TDBG_FUNC();
585132332Smarcel
586155387Sdavidxu	bzero(info, sizeof(*info));
587132332Smarcel	ret = pt_validate(th);
588132332Smarcel	if (ret)
589132332Smarcel		return (ret);
590132332Smarcel
591132332Smarcel	memset(info, 0, sizeof(*info));
592132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
593132332Smarcel		info->ti_type = TD_THR_SYSTEM;
594132332Smarcel		info->ti_lid = ta->map[th->th_tid].lwp;
595132332Smarcel		info->ti_tid = th->th_tid;
596132332Smarcel		info->ti_state = TD_THR_RUN;
597132332Smarcel		info->ti_type = TD_THR_SYSTEM;
598132332Smarcel		return (TD_OK);
599132332Smarcel	}
600158680Sdavidxu
601158680Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
602158680Sdavidxu		ta->thread_off_attr_flags,
603158680Sdavidxu		&attrflags, sizeof(attrflags));
604158680Sdavidxu	if (ret != 0)
605158680Sdavidxu		return (P2T(ret));
606133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
607133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
608132332Smarcel	if (ret != 0)
609132332Smarcel		return (P2T(ret));
610133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state,
611133802Sdavidxu	               &state, sizeof(state));
612132332Smarcel	ret = ps_pread(ta->ph,
613133802Sdavidxu	        tcb_addr + ta->thread_off_tmbx +
614133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
615132332Smarcel	        &info->ti_lid, sizeof(lwpid_t));
616132332Smarcel	if (ret != 0)
617132332Smarcel		return (P2T(ret));
618132951Sdavidxu	ret = ps_pread(ta->ph,
619133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
620133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
621132951Sdavidxu		&dflags, sizeof(dflags));
622132951Sdavidxu	if (ret != 0)
623132951Sdavidxu		return (P2T(ret));
624158680Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_tmbx +
625158680Sdavidxu		offsetof(struct kse_thr_mailbox, tm_lwp), &lwp, sizeof(lwpid_t));
626158680Sdavidxu	if (ret != 0)
627158680Sdavidxu		return (P2T(ret));
628132332Smarcel	info->ti_ta_p = th->th_ta;
629132332Smarcel	info->ti_tid = th->th_tid;
630158680Sdavidxu
631158680Sdavidxu	if (attrflags & PTHREAD_SCOPE_SYSTEM) {
632158680Sdavidxu		ret = ps_linfo(ta->ph, lwp, &linfo);
633158680Sdavidxu		if (ret == PS_OK) {
634158680Sdavidxu			info->ti_sigmask = linfo.pl_sigmask;
635158680Sdavidxu			info->ti_pending = linfo.pl_siglist;
636158680Sdavidxu		} else
637158680Sdavidxu			return (ret);
638158680Sdavidxu	} else {
639158680Sdavidxu		ret = ps_pread(ta->ph,
640158680Sdavidxu			ta->map[th->th_tid].thr + ta->thread_off_sigmask,
641158680Sdavidxu			&info->ti_sigmask, sizeof(sigset_t));
642158680Sdavidxu		if (ret)
643158680Sdavidxu			return (ret);
644158680Sdavidxu		ret = ps_pread(ta->ph,
645158680Sdavidxu			ta->map[th->th_tid].thr + ta->thread_off_sigpend,
646158680Sdavidxu			&info->ti_pending, sizeof(sigset_t));
647158680Sdavidxu		if (ret)
648158680Sdavidxu			return (ret);
649158680Sdavidxu	}
650158680Sdavidxu
651133802Sdavidxu	if (state == ta->thread_state_running)
652132332Smarcel		info->ti_state = TD_THR_RUN;
653133802Sdavidxu	else if (state == ta->thread_state_zoombie)
654133802Sdavidxu		info->ti_state = TD_THR_ZOMBIE;
655133802Sdavidxu	else
656132332Smarcel		info->ti_state = TD_THR_SLEEP;
657133047Sdavidxu	info->ti_db_suspended = ((dflags & TMDF_SUSPEND) != 0);
658132332Smarcel	info->ti_type = TD_THR_USER;
659132332Smarcel	return (0);
660132332Smarcel}
661132332Smarcel
662209689Skibstatic td_err_e
663209689Skibpt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
664209689Skib{
665209689Skib	td_err_e e;
666209689Skib
667209689Skib	e = pt_thr_old_get_info(th, (td_old_thrinfo_t *)info);
668209689Skib	bzero(&info->ti_siginfo, sizeof(info->ti_siginfo));
669209689Skib	return (e);
670209689Skib}
671209689Skib
672146818Sdfr#ifdef __i386__
673132332Smarcelstatic td_err_e
674146818Sdfrpt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
675146818Sdfr{
676146818Sdfr	const td_thragent_t *ta = th->th_ta;
677146818Sdfr	struct kse_thr_mailbox tmbx;
678146818Sdfr	psaddr_t tcb_addr, tmbx_addr, ptr;
679146818Sdfr	lwpid_t lwp;
680146818Sdfr	int ret;
681146818Sdfr
682146818Sdfr	return TD_ERR;
683146818Sdfr
684146818Sdfr	TDBG_FUNC();
685146818Sdfr
686146818Sdfr	ret = pt_validate(th);
687146818Sdfr	if (ret)
688146818Sdfr		return (ret);
689146818Sdfr
690146818Sdfr	if (ta->map[th->th_tid].type == PT_LWP) {
691146818Sdfr		ret = ps_lgetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
692146818Sdfr		return (P2T(ret));
693146818Sdfr	}
694146818Sdfr
695146818Sdfr	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
696146818Sdfr	               &tcb_addr, sizeof(tcb_addr));
697146818Sdfr	if (ret != 0)
698146818Sdfr		return (P2T(ret));
699146818Sdfr	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
700146818Sdfr	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
701146818Sdfr	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
702146818Sdfr	if (ret != 0)
703146818Sdfr		return (P2T(ret));
704146818Sdfr	if (lwp != 0) {
705146818Sdfr		ret = ps_lgetxmmregs(ta->ph, lwp, fxsave);
706146818Sdfr		return (P2T(ret));
707146818Sdfr	}
708146818Sdfr
709146818Sdfr	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
710146818Sdfr	if (ret != 0)
711146818Sdfr		return (P2T(ret));
712146818Sdfr	pt_ucontext_to_fxsave(&tmbx.tm_context, fxsave);
713146818Sdfr	return (0);
714146818Sdfr}
715146818Sdfr#endif
716146818Sdfr
717146818Sdfrstatic td_err_e
718132332Smarcelpt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
719132332Smarcel{
720132332Smarcel	const td_thragent_t *ta = th->th_ta;
721132332Smarcel	struct kse_thr_mailbox tmbx;
722132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
723132332Smarcel	lwpid_t lwp;
724132332Smarcel	int ret;
725132332Smarcel
726132332Smarcel	TDBG_FUNC();
727132332Smarcel
728132332Smarcel	ret = pt_validate(th);
729132332Smarcel	if (ret)
730132332Smarcel		return (ret);
731132332Smarcel
732132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
733132332Smarcel		ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
734132332Smarcel		return (P2T(ret));
735132332Smarcel	}
736132332Smarcel
737133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
738132951Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
739132332Smarcel	if (ret != 0)
740132332Smarcel		return (P2T(ret));
741133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
742132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
743132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
744132332Smarcel	if (ret != 0)
745132332Smarcel		return (P2T(ret));
746132332Smarcel	if (lwp != 0) {
747132332Smarcel		ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
748132332Smarcel		return (P2T(ret));
749132332Smarcel	}
750132332Smarcel
751132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
752132332Smarcel	if (ret != 0)
753132332Smarcel		return (P2T(ret));
754132332Smarcel	pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
755132332Smarcel	return (0);
756132332Smarcel}
757132332Smarcel
758132332Smarcelstatic td_err_e
759132332Smarcelpt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
760132332Smarcel{
761132332Smarcel	const td_thragent_t *ta = th->th_ta;
762132332Smarcel	struct kse_thr_mailbox tmbx;
763132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
764132332Smarcel	lwpid_t lwp;
765132332Smarcel	int ret;
766132332Smarcel
767132332Smarcel	TDBG_FUNC();
768132332Smarcel
769132332Smarcel	ret = pt_validate(th);
770132332Smarcel	if (ret)
771132332Smarcel		return (ret);
772132332Smarcel
773132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
774132332Smarcel		ret = ps_lgetregs(ta->ph,
775132332Smarcel		                  ta->map[th->th_tid].lwp, gregs);
776132332Smarcel		return (P2T(ret));
777132332Smarcel	}
778132332Smarcel
779133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
780132332Smarcel			&tcb_addr, sizeof(tcb_addr));
781132332Smarcel	if (ret != 0)
782132332Smarcel		return (P2T(ret));
783133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
784132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
785132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
786132332Smarcel	if (ret != 0)
787132332Smarcel		return (P2T(ret));
788132332Smarcel	if (lwp != 0) {
789132332Smarcel		ret = ps_lgetregs(ta->ph, lwp, gregs);
790132332Smarcel		return (P2T(ret));
791132332Smarcel	}
792132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
793132332Smarcel	if (ret != 0)
794132332Smarcel		return (P2T(ret));
795132332Smarcel	pt_ucontext_to_reg(&tmbx.tm_context, gregs);
796132332Smarcel	return (0);
797132332Smarcel}
798132332Smarcel
799146818Sdfr#ifdef __i386__
800132332Smarcelstatic td_err_e
801146818Sdfrpt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
802146818Sdfr{
803146818Sdfr	const td_thragent_t *ta = th->th_ta;
804146818Sdfr	struct kse_thr_mailbox tmbx;
805146818Sdfr	psaddr_t tcb_addr, tmbx_addr, ptr;
806146818Sdfr	lwpid_t lwp;
807146818Sdfr	int ret;
808146818Sdfr
809146818Sdfr	return TD_ERR;
810146818Sdfr
811146818Sdfr	TDBG_FUNC();
812146818Sdfr
813146818Sdfr	ret = pt_validate(th);
814146818Sdfr	if (ret)
815146818Sdfr		return (ret);
816146818Sdfr
817146818Sdfr	if (ta->map[th->th_tid].type == PT_LWP) {
818146818Sdfr		ret = ps_lsetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
819146818Sdfr		return (P2T(ret));
820146818Sdfr	}
821146818Sdfr
822146818Sdfr	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
823146818Sdfr	                ta->thread_off_tcb,
824146818Sdfr                        &tcb_addr, sizeof(tcb_addr));
825146818Sdfr	if (ret != 0)
826146818Sdfr		return (P2T(ret));
827146818Sdfr	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
828146818Sdfr	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
829146818Sdfr	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
830146818Sdfr	if (ret != 0)
831146818Sdfr		return (P2T(ret));
832146818Sdfr	if (lwp != 0) {
833146818Sdfr		ret = ps_lsetxmmregs(ta->ph, lwp, fxsave);
834146818Sdfr		return (P2T(ret));
835146818Sdfr	}
836146818Sdfr	/*
837146818Sdfr	 * Read a copy of context, this makes sure that registers
838146818Sdfr	 * not covered by structure reg won't be clobbered
839146818Sdfr	 */
840146818Sdfr	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
841146818Sdfr	if (ret != 0)
842146818Sdfr		return (P2T(ret));
843146818Sdfr
844146818Sdfr	pt_fxsave_to_ucontext(fxsave, &tmbx.tm_context);
845146818Sdfr	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
846146818Sdfr	return (P2T(ret));
847146818Sdfr}
848146818Sdfr#endif
849146818Sdfr
850146818Sdfrstatic td_err_e
851132332Smarcelpt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
852132332Smarcel{
853132332Smarcel	const td_thragent_t *ta = th->th_ta;
854132332Smarcel	struct kse_thr_mailbox tmbx;
855132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
856132332Smarcel	lwpid_t lwp;
857132332Smarcel	int ret;
858132332Smarcel
859132332Smarcel	TDBG_FUNC();
860132332Smarcel
861132332Smarcel	ret = pt_validate(th);
862132332Smarcel	if (ret)
863132332Smarcel		return (ret);
864132332Smarcel
865132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
866132332Smarcel		ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
867132332Smarcel		return (P2T(ret));
868132332Smarcel	}
869132332Smarcel
870132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
871133802Sdavidxu	                ta->thread_off_tcb,
872132332Smarcel                        &tcb_addr, sizeof(tcb_addr));
873132332Smarcel	if (ret != 0)
874132332Smarcel		return (P2T(ret));
875133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
876132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
877132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
878132332Smarcel	if (ret != 0)
879132332Smarcel		return (P2T(ret));
880132332Smarcel	if (lwp != 0) {
881132332Smarcel		ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
882132332Smarcel		return (P2T(ret));
883132332Smarcel	}
884132332Smarcel	/*
885132332Smarcel	 * Read a copy of context, this makes sure that registers
886132332Smarcel	 * not covered by structure reg won't be clobbered
887132332Smarcel	 */
888132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
889132332Smarcel	if (ret != 0)
890132332Smarcel		return (P2T(ret));
891132332Smarcel
892132332Smarcel	pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
893132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
894132332Smarcel	return (P2T(ret));
895132332Smarcel}
896132332Smarcel
897132332Smarcelstatic td_err_e
898132332Smarcelpt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
899132332Smarcel{
900132332Smarcel	const td_thragent_t *ta = th->th_ta;
901132332Smarcel	struct kse_thr_mailbox tmbx;
902132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
903132332Smarcel	lwpid_t lwp;
904132332Smarcel	int ret;
905132332Smarcel
906132332Smarcel	TDBG_FUNC();
907132332Smarcel
908132332Smarcel	ret = pt_validate(th);
909132332Smarcel	if (ret)
910132332Smarcel		return (ret);
911132332Smarcel
912132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
913132332Smarcel		ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
914132332Smarcel		return (P2T(ret));
915132332Smarcel	}
916132332Smarcel
917132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
918133802Sdavidxu	                ta->thread_off_tcb,
919132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
920132332Smarcel	if (ret != 0)
921132332Smarcel		return (P2T(ret));
922133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
923132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
924132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
925132332Smarcel	if (ret != 0)
926132332Smarcel		return (P2T(ret));
927132332Smarcel	if (lwp != 0) {
928132332Smarcel		ret = ps_lsetregs(ta->ph, lwp, gregs);
929132332Smarcel		return (P2T(ret));
930132332Smarcel	}
931132332Smarcel
932132332Smarcel	/*
933132332Smarcel	 * Read a copy of context, make sure that registers
934132332Smarcel	 * not covered by structure reg won't be clobbered
935132332Smarcel	 */
936132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
937132332Smarcel	if (ret != 0)
938132332Smarcel		return (P2T(ret));
939132332Smarcel	pt_reg_to_ucontext(gregs, &tmbx.tm_context);
940132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
941132332Smarcel	return (P2T(ret));
942132332Smarcel}
943132332Smarcel
944132332Smarcelstatic td_err_e
945181341Smarcelpt_thr_event_enable(const td_thrhandle_t *th __unused, int en __unused)
946132332Smarcel{
947132332Smarcel	TDBG_FUNC();
948144922Sdavidxu	return (0);
949132332Smarcel}
950132332Smarcel
951132332Smarcelstatic td_err_e
952181341Smarcelpt_thr_set_event(const td_thrhandle_t *th __unused,
953181341Smarcel    td_thr_events_t *setp __unused)
954132332Smarcel{
955132332Smarcel	TDBG_FUNC();
956144922Sdavidxu	return (0);
957132332Smarcel}
958132332Smarcel
959132332Smarcelstatic td_err_e
960181341Smarcelpt_thr_clear_event(const td_thrhandle_t *th __unused,
961181341Smarcel    td_thr_events_t *setp __unused)
962132332Smarcel{
963132332Smarcel	TDBG_FUNC();
964144922Sdavidxu	return (0);
965132332Smarcel}
966132332Smarcel
967132332Smarcelstatic td_err_e
968181341Smarcelpt_thr_event_getmsg(const td_thrhandle_t *th __unused,
969181341Smarcel    td_event_msg_t *msg __unused)
970132332Smarcel{
971132332Smarcel	TDBG_FUNC();
972132332Smarcel	return (TD_NOMSG);
973132332Smarcel}
974132332Smarcel
975132332Smarcelstatic td_err_e
976132332Smarcelpt_thr_sstep(const td_thrhandle_t *th, int step)
977132332Smarcel{
978132332Smarcel	const td_thragent_t *ta = th->th_ta;
979132332Smarcel	struct kse_thr_mailbox tmbx;
980132332Smarcel	struct reg regs;
981132332Smarcel	psaddr_t tcb_addr, tmbx_addr;
982132951Sdavidxu	uint32_t dflags;
983132332Smarcel	lwpid_t lwp;
984132332Smarcel	int ret;
985132332Smarcel
986132332Smarcel	TDBG_FUNC();
987132332Smarcel
988132332Smarcel	ret = pt_validate(th);
989132332Smarcel	if (ret)
990132332Smarcel		return (ret);
991132332Smarcel
992132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP)
993132332Smarcel		return (TD_BADTH);
994132332Smarcel
995132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
996133802Sdavidxu	                ta->thread_off_tcb,
997132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
998132332Smarcel	if (ret != 0)
999132332Smarcel		return (P2T(ret));
1000132332Smarcel
1001132332Smarcel	/* Clear or set single step flag in thread mailbox */
1002133802Sdavidxu	ret = ps_pread(ta->ph,
1003133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1004133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
1005133802Sdavidxu		&dflags, sizeof(uint32_t));
1006132951Sdavidxu	if (ret != 0)
1007132951Sdavidxu		return (P2T(ret));
1008132951Sdavidxu	if (step != 0)
1009132951Sdavidxu		dflags |= TMDF_SSTEP;
1010132951Sdavidxu	else
1011132951Sdavidxu		dflags &= ~TMDF_SSTEP;
1012133802Sdavidxu	ret = ps_pwrite(ta->ph,
1013133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1014133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
1015133802Sdavidxu	        &dflags, sizeof(uint32_t));
1016132332Smarcel	if (ret != 0)
1017132332Smarcel		return (P2T(ret));
1018132332Smarcel	/* Get lwp */
1019133802Sdavidxu	ret = ps_pread(ta->ph,
1020133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1021133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
1022133802Sdavidxu		&lwp, sizeof(lwpid_t));
1023132332Smarcel	if (ret != 0)
1024132332Smarcel		return (P2T(ret));
1025132332Smarcel	if (lwp != 0)
1026132951Sdavidxu		return (0);
1027132332Smarcel
1028133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
1029132332Smarcel	/*
1030132332Smarcel	 * context is in userland, some architectures store
1031132332Smarcel	 * single step status in registers, we should change
1032132332Smarcel	 * these registers.
1033132332Smarcel	 */
1034132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
1035132332Smarcel	if (ret == 0) {
1036132332Smarcel		pt_ucontext_to_reg(&tmbx.tm_context, &regs);
1037132332Smarcel		/* only write out if it is really changed. */
1038132332Smarcel		if (pt_reg_sstep(&regs, step) != 0) {
1039132332Smarcel			pt_reg_to_ucontext(&regs, &tmbx.tm_context);
1040132332Smarcel			ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
1041132332Smarcel			                 sizeof(tmbx));
1042132332Smarcel		}
1043132332Smarcel	}
1044132332Smarcel	return (P2T(ret));
1045132332Smarcel}
1046132332Smarcel
1047132332Smarcelstatic void
1048132332Smarcelpt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
1049132332Smarcel{
1050132332Smarcel	int i;
1051132332Smarcel
1052132332Smarcel	for (i = 0; i < ta->map_len; ++i) {
1053132332Smarcel		if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
1054132332Smarcel			ta->map[i].type = PT_NONE;
1055132332Smarcel			return;
1056132332Smarcel		}
1057132332Smarcel	}
1058132332Smarcel}
1059132332Smarcel
1060132332Smarcelstatic int
1061132332Smarcelpt_validate(const td_thrhandle_t *th)
1062132332Smarcel{
1063132332Smarcel
1064132332Smarcel	if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
1065132332Smarcel	    th->th_ta->map[th->th_tid].type == PT_NONE)
1066132332Smarcel		return (TD_NOTHR);
1067132332Smarcel	return (TD_OK);
1068132332Smarcel}
1069132332Smarcel
1070181059Smarcelstatic td_err_e
1071180982Smarcelpt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
1072180982Smarcel    psaddr_t *address)
1073133342Sdavidxu{
1074133342Sdavidxu	const td_thragent_t *ta = th->th_ta;
1075180982Smarcel	psaddr_t dtv_addr, obj_entry, tcb_addr;
1076133342Sdavidxu	int tls_index, ret;
1077133342Sdavidxu
1078133342Sdavidxu	/* linkmap is a member of Obj_Entry */
1079180982Smarcel	obj_entry = _linkmap - ta->thread_off_linkmap;
1080133342Sdavidxu
1081133342Sdavidxu	/* get tlsindex of the object file */
1082133342Sdavidxu	ret = ps_pread(ta->ph,
1083133802Sdavidxu		obj_entry + ta->thread_off_tlsindex,
1084133342Sdavidxu		&tls_index, sizeof(tls_index));
1085133342Sdavidxu	if (ret != 0)
1086133342Sdavidxu		return (P2T(ret));
1087133342Sdavidxu
1088133342Sdavidxu	/* get thread tcb */
1089133342Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
1090133802Sdavidxu		ta->thread_off_tcb,
1091133342Sdavidxu		&tcb_addr, sizeof(tcb_addr));
1092133342Sdavidxu	if (ret != 0)
1093133342Sdavidxu		return (P2T(ret));
1094133342Sdavidxu
1095133342Sdavidxu	/* get dtv array address */
1096133802Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
1097133342Sdavidxu		&dtv_addr, sizeof(dtv_addr));
1098133342Sdavidxu	if (ret != 0)
1099133342Sdavidxu		return (P2T(ret));
1100133342Sdavidxu	/* now get the object's tls block base address */
1101180982Smarcel	ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index + 1),
1102180982Smarcel	    address, sizeof(*address));
1103133342Sdavidxu	if (ret != 0)
1104133342Sdavidxu		return (P2T(ret));
1105133342Sdavidxu
1106133342Sdavidxu	*address += offset;
1107133342Sdavidxu	return (TD_OK);
1108133342Sdavidxu}
1109133342Sdavidxu
1110132332Smarcelstruct ta_ops libpthread_db_ops = {
1111132332Smarcel	.to_init		= pt_init,
1112132332Smarcel	.to_ta_clear_event	= pt_ta_clear_event,
1113132332Smarcel	.to_ta_delete		= pt_ta_delete,
1114132332Smarcel	.to_ta_event_addr	= pt_ta_event_addr,
1115132332Smarcel	.to_ta_event_getmsg	= pt_ta_event_getmsg,
1116132332Smarcel	.to_ta_map_id2thr	= pt_ta_map_id2thr,
1117132332Smarcel	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
1118132332Smarcel	.to_ta_new		= pt_ta_new,
1119132332Smarcel	.to_ta_set_event	= pt_ta_set_event,
1120132332Smarcel	.to_ta_thr_iter		= pt_ta_thr_iter,
1121132332Smarcel	.to_ta_tsd_iter		= pt_ta_tsd_iter,
1122132332Smarcel	.to_thr_clear_event	= pt_thr_clear_event,
1123132332Smarcel	.to_thr_dbresume	= pt_thr_dbresume,
1124132332Smarcel	.to_thr_dbsuspend	= pt_thr_dbsuspend,
1125132332Smarcel	.to_thr_event_enable	= pt_thr_event_enable,
1126132332Smarcel	.to_thr_event_getmsg	= pt_thr_event_getmsg,
1127209689Skib	.to_thr_old_get_info	= pt_thr_old_get_info,
1128132332Smarcel	.to_thr_get_info	= pt_thr_get_info,
1129132332Smarcel	.to_thr_getfpregs	= pt_thr_getfpregs,
1130132332Smarcel	.to_thr_getgregs	= pt_thr_getgregs,
1131132332Smarcel	.to_thr_set_event	= pt_thr_set_event,
1132132332Smarcel	.to_thr_setfpregs	= pt_thr_setfpregs,
1133132332Smarcel	.to_thr_setgregs	= pt_thr_setgregs,
1134132332Smarcel	.to_thr_validate	= pt_thr_validate,
1135133342Sdavidxu	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
1136132332Smarcel
1137132332Smarcel	/* FreeBSD specific extensions. */
1138132332Smarcel	.to_thr_sstep		= pt_thr_sstep,
1139146818Sdfr#ifdef __i386__
1140146818Sdfr	.to_thr_getxmmregs	= pt_thr_getxmmregs,
1141146818Sdfr	.to_thr_setxmmregs	= pt_thr_setxmmregs,
1142146818Sdfr#endif
1143132332Smarcel};
1144177490Sdavidxu
1145177490SdavidxuDATA_SET(__ta_ops, libpthread_db_ops);
1146