summaryrefslogtreecommitdiff
path: root/lib/libpthread/arch/i386/uthread_machdep.c
blob: 8d9d5da9da5abdf4001e52798c36eaad12efa11b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*	 $OpenBSD: uthread_machdep.c,v 1.6 2008/01/28 18:48:41 kettenis Exp $	*/
/* David Leonard, <d@csee.uq.edu.au>. Public domain. */

/*
 * Machine-dependent thread state functions for OpenBSD/i386.
 */

#include <sys/param.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#include <pthread.h>
#include "pthread_private.h"

struct frame {
	int	fr_gs;
	int	fr_fs;
	int	fr_es;
	int	fr_ds;

	int	fr_edi;
	int	fr_esi;
	int	fr_ebp;
	int	fr_esp;
	int	fr_ebx;
	int	fr_edx;
	int	fr_ecx;
	int	fr_eax;

	int	fr_eip;
	int	fr_cs;		/* XXX unreachable? */
};

#define copyreg(reg, lval) \
	__asm__("mov %%" #reg ", %0" : "=g"(lval))

static int _thread_machdep_osfxsr(void);

static int
_thread_machdep_osfxsr(void)
{
	int mib[] = { CTL_MACHDEP, CPU_OSFXSR };
	static int sse = -1;
	size_t len;
	int val;

	if (sse == -1) {
		len = sizeof (val);
		if (sysctl(mib, 2, &val, &len, NULL, 0) == -1)
			return (0);
		if (val)
			sse = 1;
		else
			sse = 0;
	}
	return (sse);
}

/*
 * Given a stack and an entry function, initialise a state
 * structure that can be later switched to.
 */
void
_thread_machdep_init(struct _machdep_state* statep, void *base, int len,
		     void (*entry)(void))
{
	struct frame *f;

	/*
	 * Locate the initial frame at the top of the stack.  For the
	 * stack to end up properly (16-byte) aligned, we need to
	 * align the frame at an odd 8-byte boundary.
	 */
	f = (struct frame *)((((int)base + len - sizeof *f) & ~15) - 8);

	/* Set up initial frame */
	f->fr_esp = (int)&f->fr_edi;
	copyreg(cs, f->fr_cs);
	copyreg(ds, f->fr_ds);
	copyreg(es, f->fr_es);
	copyreg(fs, f->fr_fs);
	copyreg(gs, f->fr_gs);
	f->fr_ebp = (int)-1;
	f->fr_eip = (int)entry;

	statep->esp = (int)f;

	_thread_machdep_save_float_state(statep);
	/*
	 * The current thread float state is saved into the new thread stack.
	 * Later pthread_create calls _thread_kern_sched which saves the current
	 * thread float state again into its own stack. However all float state
	 * saves must be balanced with a restore on i386 due to the fninit().
	 * Restore the current thread float state here so that the next save
	 * gets the correct state. 
	 */
	_thread_machdep_restore_float_state(statep);
}

/*
 * Floating point save restore copied from code in npx.c
 * (without really understanding what it does).
 */
#define	fldcw(addr)		__asm("fldcw %0"  : : "m" (*addr))
#define	fnsave(addr)		__asm("fnsave %0" : "=m" (*addr))
#define	fninit()		__asm("fninit")
#define	frstor(addr)		__asm("frstor %0" : : "m" (*addr))
#define	fxsave(addr)		__asm("fxsave %0" : "=m" (*addr))
#define	fxrstor(addr)		__asm("fxrstor %0" : : "m" (*addr))
#define	fwait()			__asm("fwait")

void
_thread_machdep_save_float_state(struct _machdep_state *ms)
{
	union savefpu *addr = &ms->fpreg;

	if (_thread_machdep_osfxsr()) {
		fwait();
		fxsave(&addr->sv_xmm);
		fninit();
	} else
		fnsave(&addr->sv_87);
	fwait();
}

void
_thread_machdep_restore_float_state(struct _machdep_state *ms)
{
	union savefpu *addr = &ms->fpreg;

	if (_thread_machdep_osfxsr()) {
		fxrstor(&addr->sv_xmm);
		fwait();
	} else
		frstor(&addr->sv_87);

}